|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
2
|
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|
3
|
+
// Copyright (c) 2011-2023 The Peercoin developers
|
|
3
4
|
// Distributed under the MIT software license, see the accompanying
|
|
4
5
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
6
|
|
|
@@ -355,8 +355,8 @@ data CreateChecksum(Encoding encoding, const std::string& hrp, const data& value
|
|
|
355
355
|
|
|
356
356
|
/** Encode a Bech32 or Bech32m string. */
|
|
357
357
|
std::string Encode(Encoding encoding, const std::string& hrp, const data& values) {
|
|
358
|
-
// First ensure that the HRP is all lowercase. BIP-173
|
|
359
|
-
// to return a lowercase Bech32
|
|
358
|
+
// First ensure that the HRP is all lowercase. BIP-173 requires an encoder
|
|
359
|
+
// to return a lowercase Bech32 string, but if given an uppercase HRP, the
|
|
360
360
|
// result will always be invalid.
|
|
361
361
|
for (const char& c : hrp) assert(c < 'A' || c > 'Z');
|
|
362
362
|
data checksum = CreateChecksum(encoding, hrp, values);
|
|
@@ -42,7 +42,7 @@ struct DecodeResult
|
|
|
42
42
|
DecodeResult(Encoding enc, std::string&& h, std::vector<uint8_t>&& d) : encoding(enc), hrp(std::move(h)), data(std::move(d)) {}
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
/** Decode a Bech32
|
|
45
|
+
/** Decode a Bech32 string. */
|
|
46
46
|
DecodeResult Decode(const std::string& str);
|
|
47
47
|
|
|
48
48
|
/** Return the positions of errors in a Bech32 string. */
|
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
+
// Copyright (c) 2009-2012 The Bitcoin developers
|
|
3
|
+
// Copyright (c) 2011-2023 The Peercoin developers
|
|
4
|
+
// Copyright (c) 2018-2018 The Emercoin developers
|
|
5
|
+
#ifndef BITCOIN_BIGNUM_H
|
|
6
|
+
#define BITCOIN_BIGNUM_H
|
|
7
|
+
|
|
8
|
+
#include <stdexcept>
|
|
9
|
+
#include <vector>
|
|
10
|
+
#include <openssl/bn.h>
|
|
11
|
+
|
|
12
|
+
/** Errors thrown by the bignum class */
|
|
13
|
+
class bignum_error : public std::runtime_error
|
|
14
|
+
{
|
|
15
|
+
public:
|
|
16
|
+
explicit bignum_error(const std::string& str) : std::runtime_error(str) {}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/** RAII encapsulated BN_CTX (OpenSSL bignum context) */
|
|
21
|
+
class CAutoBN_CTX
|
|
22
|
+
{
|
|
23
|
+
protected:
|
|
24
|
+
BN_CTX* pctx;
|
|
25
|
+
BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; }
|
|
26
|
+
|
|
27
|
+
public:
|
|
28
|
+
CAutoBN_CTX()
|
|
29
|
+
{
|
|
30
|
+
pctx = BN_CTX_new();
|
|
31
|
+
if (pctx == NULL)
|
|
32
|
+
throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
~CAutoBN_CTX()
|
|
36
|
+
{
|
|
37
|
+
if (pctx != NULL)
|
|
38
|
+
BN_CTX_free(pctx);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
operator BN_CTX*() { return pctx; }
|
|
42
|
+
BN_CTX& operator*() { return *pctx; }
|
|
43
|
+
BN_CTX** operator&() { return &pctx; }
|
|
44
|
+
bool operator!() { return (pctx == NULL); }
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
/** C++ wrapper for BIGNUM (OpenSSL bignum) */
|
|
49
|
+
class CBigNum
|
|
50
|
+
{
|
|
51
|
+
private:
|
|
52
|
+
BIGNUM *self;
|
|
53
|
+
|
|
54
|
+
void init()
|
|
55
|
+
{
|
|
56
|
+
if (self) BN_clear_free(self);
|
|
57
|
+
self = BN_new();
|
|
58
|
+
if (!self)
|
|
59
|
+
throw bignum_error("CBigNum::init() : BN_new() returned NULL");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public:
|
|
63
|
+
BIGNUM* get() { return self; }
|
|
64
|
+
const BIGNUM* cget() const { return self; }
|
|
65
|
+
|
|
66
|
+
CBigNum() : self(NULL)
|
|
67
|
+
{
|
|
68
|
+
init();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
CBigNum(const CBigNum& b) : self(NULL)
|
|
72
|
+
{
|
|
73
|
+
init();
|
|
74
|
+
if (!BN_copy(self, b.cget()))
|
|
75
|
+
{
|
|
76
|
+
BN_clear_free(self);
|
|
77
|
+
throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
CBigNum& operator=(const CBigNum& b)
|
|
82
|
+
{
|
|
83
|
+
if (!BN_copy(self, b.cget()))
|
|
84
|
+
throw bignum_error("CBigNum::operator= : BN_copy failed");
|
|
85
|
+
return (*this);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
~CBigNum()
|
|
89
|
+
{
|
|
90
|
+
if (self) BN_clear_free(self);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
//CBigNum(char n) is not portable. Use 'signed char' or 'unsigned char'.
|
|
94
|
+
CBigNum(signed char n) : self(NULL) { init(); if (n >= 0) setulong(n); else setint64(n); }
|
|
95
|
+
CBigNum(short n) : self(NULL) { init(); if (n >= 0) setulong(n); else setint64(n); }
|
|
96
|
+
CBigNum(int n) : self(NULL) { init(); if (n >= 0) setulong(n); else setint64(n); }
|
|
97
|
+
CBigNum(int64_t n) : self(NULL) { init(); setint64(n); }
|
|
98
|
+
CBigNum(unsigned char n) : self(NULL) { init(); setulong(n); }
|
|
99
|
+
CBigNum(unsigned short n) : self(NULL) { init(); setulong(n); }
|
|
100
|
+
CBigNum(unsigned int n) : self(NULL) { init(); setulong(n); }
|
|
101
|
+
CBigNum(uint64_t n) : self(NULL) { init(); setuint64(n); }
|
|
102
|
+
explicit CBigNum(uint256 n) : self(NULL) { init(); setuint256(n); }
|
|
103
|
+
|
|
104
|
+
explicit CBigNum(const std::vector<unsigned char>& vch) : self(NULL)
|
|
105
|
+
{
|
|
106
|
+
init();
|
|
107
|
+
setvch(vch);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void setulong(unsigned long n)
|
|
111
|
+
{
|
|
112
|
+
if (!BN_set_word(self, n))
|
|
113
|
+
throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
unsigned long getulong() const
|
|
117
|
+
{
|
|
118
|
+
return BN_get_word(self);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
unsigned int getuint() const
|
|
122
|
+
{
|
|
123
|
+
return BN_get_word(self);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
int getint() const
|
|
127
|
+
{
|
|
128
|
+
unsigned long n = BN_get_word(self);
|
|
129
|
+
if (!BN_is_negative(self))
|
|
130
|
+
return (n > (unsigned long)std::numeric_limits<int>::max() ? std::numeric_limits<int>::max() : n);
|
|
131
|
+
else
|
|
132
|
+
return (n > (unsigned long)std::numeric_limits<int>::max() ? std::numeric_limits<int>::min() : -(int)n);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
void setint64(int64_t n)
|
|
136
|
+
{
|
|
137
|
+
unsigned char pcx[16], *p = pcx + 15; *p = 0;
|
|
138
|
+
uint8_t neg = 0;
|
|
139
|
+
uint64_t m = n; // to correct care -0
|
|
140
|
+
if(n < 0)
|
|
141
|
+
m = -n, neg = 0x80;
|
|
142
|
+
while(m) {
|
|
143
|
+
*--p = m;
|
|
144
|
+
m >>= 8;
|
|
145
|
+
}
|
|
146
|
+
if((signed char)*p < 0)
|
|
147
|
+
*--p = neg;
|
|
148
|
+
*p |= neg;
|
|
149
|
+
n = pcx + 15 - p;
|
|
150
|
+
*--p = n;
|
|
151
|
+
*--p = 0;
|
|
152
|
+
*--p = 0;
|
|
153
|
+
*--p = 0;
|
|
154
|
+
BN_mpi2bn(p, pcx + 15 - p, self);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
void setuint64(uint64_t n)
|
|
158
|
+
{
|
|
159
|
+
unsigned char pcx[16], *p = pcx + 15; *p = 0;
|
|
160
|
+
while(n) {
|
|
161
|
+
*--p = n;
|
|
162
|
+
n >>= 8;
|
|
163
|
+
}
|
|
164
|
+
if((signed char)*p < 0)
|
|
165
|
+
*--p = 0;
|
|
166
|
+
n = pcx + 15 - p;
|
|
167
|
+
*--p = n;
|
|
168
|
+
*--p = 0;
|
|
169
|
+
*--p = 0;
|
|
170
|
+
*--p = 0;
|
|
171
|
+
BN_mpi2bn(p, pcx + 15 - p, self);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
uint64_t getuint64()
|
|
175
|
+
{
|
|
176
|
+
unsigned int nSize = BN_bn2mpi(self, NULL);
|
|
177
|
+
if (nSize < 4)
|
|
178
|
+
return 0;
|
|
179
|
+
std::vector<unsigned char> vch(nSize);
|
|
180
|
+
BN_bn2mpi(self, &vch[0]);
|
|
181
|
+
if (vch.size() > 4)
|
|
182
|
+
vch[4] &= 0x7f;
|
|
183
|
+
uint64_t n = 0;
|
|
184
|
+
for (unsigned int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--)
|
|
185
|
+
((unsigned char*)&n)[i] = vch[j];
|
|
186
|
+
return n;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
void setuint256(uint256 n)
|
|
190
|
+
{
|
|
191
|
+
unsigned char pch[sizeof(n) + 6];
|
|
192
|
+
unsigned char* p = pch + 4;
|
|
193
|
+
bool fLeadingZeroes = true;
|
|
194
|
+
unsigned char* pbegin = (unsigned char*)&n;
|
|
195
|
+
unsigned char* psrc = pbegin + sizeof(n);
|
|
196
|
+
while (psrc != pbegin)
|
|
197
|
+
{
|
|
198
|
+
unsigned char c = *(--psrc);
|
|
199
|
+
if (fLeadingZeroes)
|
|
200
|
+
{
|
|
201
|
+
if (c == 0)
|
|
202
|
+
continue;
|
|
203
|
+
if (c & 0x80)
|
|
204
|
+
*p++ = 0;
|
|
205
|
+
fLeadingZeroes = false;
|
|
206
|
+
}
|
|
207
|
+
*p++ = c;
|
|
208
|
+
}
|
|
209
|
+
unsigned int nSize = p - (pch + 4);
|
|
210
|
+
pch[0] = (nSize >> 24) & 0xff;
|
|
211
|
+
pch[1] = (nSize >> 16) & 0xff;
|
|
212
|
+
pch[2] = (nSize >> 8) & 0xff;
|
|
213
|
+
pch[3] = (nSize >> 0) & 0xff;
|
|
214
|
+
BN_mpi2bn(pch, p - pch, self);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
uint256 getuint256() const
|
|
218
|
+
{
|
|
219
|
+
unsigned int nSize = BN_bn2mpi(self, NULL);
|
|
220
|
+
if (nSize < 4)
|
|
221
|
+
return uint256();
|
|
222
|
+
std::vector<unsigned char> vch(nSize);
|
|
223
|
+
BN_bn2mpi(self, &vch[0]);
|
|
224
|
+
if (vch.size() > 4)
|
|
225
|
+
vch[4] &= 0x7f;
|
|
226
|
+
uint256 n = uint256();
|
|
227
|
+
for (unsigned int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--)
|
|
228
|
+
((unsigned char*)&n)[i] = vch[j];
|
|
229
|
+
return n;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
void setvch(const std::vector<unsigned char>& vch)
|
|
233
|
+
{
|
|
234
|
+
std::vector<unsigned char> vch2(vch.size() + 4);
|
|
235
|
+
unsigned int nSize = vch.size();
|
|
236
|
+
// BIGNUM's byte stream format expects 4 bytes of
|
|
237
|
+
// big endian size data info at the front
|
|
238
|
+
vch2[0] = (nSize >> 24) & 0xff;
|
|
239
|
+
vch2[1] = (nSize >> 16) & 0xff;
|
|
240
|
+
vch2[2] = (nSize >> 8) & 0xff;
|
|
241
|
+
vch2[3] = (nSize >> 0) & 0xff;
|
|
242
|
+
// swap data to big endian
|
|
243
|
+
reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4);
|
|
244
|
+
BN_mpi2bn(&vch2[0], vch2.size(), self);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
std::vector<unsigned char> getvch() const
|
|
248
|
+
{
|
|
249
|
+
unsigned int nSize = BN_bn2mpi(self, NULL);
|
|
250
|
+
if (nSize <= 4)
|
|
251
|
+
return std::vector<unsigned char>();
|
|
252
|
+
std::vector<unsigned char> vch(nSize);
|
|
253
|
+
BN_bn2mpi(self, &vch[0]);
|
|
254
|
+
vch.erase(vch.begin(), vch.begin() + 4);
|
|
255
|
+
reverse(vch.begin(), vch.end());
|
|
256
|
+
return vch;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// The "compact" format is a representation of a whole
|
|
260
|
+
// number N using an unsigned 32bit number similar to a
|
|
261
|
+
// floating point format.
|
|
262
|
+
// The most significant 8 bits are the unsigned exponent of base 256.
|
|
263
|
+
// This exponent can be thought of as "number of bytes of N".
|
|
264
|
+
// The lower 23 bits are the mantissa.
|
|
265
|
+
// Bit number 24 (0x800000) represents the sign of N.
|
|
266
|
+
// N = (-1^sign) * mantissa * 256^(exponent-3)
|
|
267
|
+
//
|
|
268
|
+
// Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn().
|
|
269
|
+
// MPI uses the most significant bit of the first byte as sign.
|
|
270
|
+
// Thus 0x1234560000 is compact (0x05123456)
|
|
271
|
+
// and 0xc0de000000 is compact (0x0600c0de)
|
|
272
|
+
// (0x05c0de00) would be -0x40de000000
|
|
273
|
+
//
|
|
274
|
+
// Bitcoin only uses this "compact" format for encoding difficulty
|
|
275
|
+
// targets, which are unsigned 256bit quantities. Thus, all the
|
|
276
|
+
// complexities of the sign bit and using base 256 are probably an
|
|
277
|
+
// implementation accident.
|
|
278
|
+
//
|
|
279
|
+
// This implementation directly uses shifts instead of going
|
|
280
|
+
// through an intermediate MPI representation.
|
|
281
|
+
CBigNum& SetCompact(unsigned int nCompact)
|
|
282
|
+
{
|
|
283
|
+
unsigned int nSize = nCompact >> 24;
|
|
284
|
+
bool fNegative =(nCompact & 0x00800000) != 0;
|
|
285
|
+
unsigned int nWord = nCompact & 0x007fffff;
|
|
286
|
+
if (nSize <= 3)
|
|
287
|
+
{
|
|
288
|
+
nWord >>= 8*(3-nSize);
|
|
289
|
+
BN_set_word(self, nWord);
|
|
290
|
+
}
|
|
291
|
+
else
|
|
292
|
+
{
|
|
293
|
+
BN_set_word(self, nWord);
|
|
294
|
+
BN_lshift(self, self, 8*(nSize-3));
|
|
295
|
+
}
|
|
296
|
+
BN_set_negative(self, fNegative);
|
|
297
|
+
return *this;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
unsigned int GetCompact() const
|
|
301
|
+
{
|
|
302
|
+
unsigned int nSize = BN_num_bytes(self);
|
|
303
|
+
unsigned int nCompact = 0;
|
|
304
|
+
if (nSize <= 3)
|
|
305
|
+
nCompact = BN_get_word(self) << 8*(3-nSize);
|
|
306
|
+
else
|
|
307
|
+
{
|
|
308
|
+
CBigNum bn;
|
|
309
|
+
BN_rshift(bn.get(), self, 8*(nSize-3));
|
|
310
|
+
nCompact = BN_get_word(bn.cget());
|
|
311
|
+
}
|
|
312
|
+
// The 0x00800000 bit denotes the sign.
|
|
313
|
+
// Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
|
|
314
|
+
if (nCompact & 0x00800000)
|
|
315
|
+
{
|
|
316
|
+
nCompact >>= 8;
|
|
317
|
+
nSize++;
|
|
318
|
+
}
|
|
319
|
+
nCompact |= nSize << 24;
|
|
320
|
+
nCompact |= (BN_is_negative(self) ? 0x00800000 : 0);
|
|
321
|
+
return nCompact;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
void SetHex(const std::string& str)
|
|
325
|
+
{
|
|
326
|
+
// skip 0x
|
|
327
|
+
const char* psz = str.c_str();
|
|
328
|
+
while (isspace(*psz))
|
|
329
|
+
psz++;
|
|
330
|
+
bool fNegative = false;
|
|
331
|
+
if (*psz == '-')
|
|
332
|
+
{
|
|
333
|
+
fNegative = true;
|
|
334
|
+
psz++;
|
|
335
|
+
}
|
|
336
|
+
if (psz[0] == '0' && tolower(psz[1]) == 'x')
|
|
337
|
+
psz += 2;
|
|
338
|
+
while (isspace(*psz))
|
|
339
|
+
psz++;
|
|
340
|
+
|
|
341
|
+
// hex string to bignum
|
|
342
|
+
static const signed char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 };
|
|
343
|
+
*this = 0;
|
|
344
|
+
while (isxdigit(*psz))
|
|
345
|
+
{
|
|
346
|
+
*this <<= 4;
|
|
347
|
+
int n = phexdigit[(unsigned char)*psz++];
|
|
348
|
+
*this += n;
|
|
349
|
+
}
|
|
350
|
+
if (fNegative)
|
|
351
|
+
*this = 0 - *this;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
std::string ToString(int nBase=10) const
|
|
355
|
+
{
|
|
356
|
+
CAutoBN_CTX pctx;
|
|
357
|
+
CBigNum bnBase = nBase;
|
|
358
|
+
CBigNum bn0 = 0;
|
|
359
|
+
std::string str;
|
|
360
|
+
CBigNum bn = *this;
|
|
361
|
+
BN_set_negative(bn.get(), false);
|
|
362
|
+
CBigNum dv;
|
|
363
|
+
CBigNum rem;
|
|
364
|
+
if (BN_cmp(bn.get(), bn0.cget()) == 0)
|
|
365
|
+
return "0";
|
|
366
|
+
while (BN_cmp(bn.get(), bn0.cget()) > 0)
|
|
367
|
+
{
|
|
368
|
+
if (!BN_div(dv.get(), rem.get(), bn.cget(), bnBase.cget(), pctx))
|
|
369
|
+
throw bignum_error("CBigNum::ToString() : BN_div failed");
|
|
370
|
+
bn = dv;
|
|
371
|
+
unsigned int c = rem.getulong();
|
|
372
|
+
str += "0123456789abcdef"[c];
|
|
373
|
+
}
|
|
374
|
+
if (BN_is_negative(self))
|
|
375
|
+
str += "-";
|
|
376
|
+
reverse(str.begin(), str.end());
|
|
377
|
+
return str;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
std::string GetHex() const
|
|
381
|
+
{
|
|
382
|
+
return ToString(16);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
unsigned int GetSerializeSize(int nType=0, int nVersion=PROTOCOL_VERSION) const
|
|
386
|
+
{
|
|
387
|
+
return ::GetSerializeSize(getvch(), nType, nVersion);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
template<typename Stream>
|
|
391
|
+
void Serialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) const
|
|
392
|
+
{
|
|
393
|
+
::Serialize(s, getvch(), nType, nVersion);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
template<typename Stream>
|
|
397
|
+
void Unserialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION)
|
|
398
|
+
{
|
|
399
|
+
std::vector<unsigned char> vch;
|
|
400
|
+
::Unserialize(s, vch, nType, nVersion);
|
|
401
|
+
setvch(vch);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
bool operator!() const
|
|
406
|
+
{
|
|
407
|
+
return BN_is_zero(self);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
CBigNum& operator+=(const CBigNum& b)
|
|
411
|
+
{
|
|
412
|
+
if (!BN_add(self, self, b.cget()))
|
|
413
|
+
throw bignum_error("CBigNum::operator+= : BN_add failed");
|
|
414
|
+
return *this;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
CBigNum& operator-=(const CBigNum& b)
|
|
418
|
+
{
|
|
419
|
+
*this = *this - b;
|
|
420
|
+
return *this;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
CBigNum& operator*=(const CBigNum& b)
|
|
424
|
+
{
|
|
425
|
+
CAutoBN_CTX pctx;
|
|
426
|
+
if (!BN_mul(self, self, b.cget(), pctx))
|
|
427
|
+
throw bignum_error("CBigNum::operator*= : BN_mul failed");
|
|
428
|
+
return *this;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
CBigNum& operator/=(const CBigNum& b)
|
|
432
|
+
{
|
|
433
|
+
*this = *this / b;
|
|
434
|
+
return *this;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
CBigNum& operator%=(const CBigNum& b)
|
|
438
|
+
{
|
|
439
|
+
*this = *this % b;
|
|
440
|
+
return *this;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
CBigNum& operator<<=(unsigned int shift)
|
|
444
|
+
{
|
|
445
|
+
if (!BN_lshift(self, self, shift))
|
|
446
|
+
throw bignum_error("CBigNum:operator<<= : BN_lshift failed");
|
|
447
|
+
return *this;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
CBigNum& operator>>=(unsigned int shift)
|
|
451
|
+
{
|
|
452
|
+
// Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number
|
|
453
|
+
// if built on ubuntu 9.04 or 9.10, probably depends on version of OpenSSL
|
|
454
|
+
CBigNum a = 1;
|
|
455
|
+
a <<= shift;
|
|
456
|
+
if (BN_cmp(a.cget(), self) > 0)
|
|
457
|
+
{
|
|
458
|
+
*this = 0;
|
|
459
|
+
return *this;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (!BN_rshift(self, self, shift))
|
|
463
|
+
throw bignum_error("CBigNum:operator>>= : BN_rshift failed");
|
|
464
|
+
return *this;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
CBigNum& operator++()
|
|
469
|
+
{
|
|
470
|
+
// prefix operator
|
|
471
|
+
if (!BN_add(self, self, BN_value_one()))
|
|
472
|
+
throw bignum_error("CBigNum::operator++ : BN_add failed");
|
|
473
|
+
return *this;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const CBigNum operator++(int)
|
|
477
|
+
{
|
|
478
|
+
// postfix operator
|
|
479
|
+
const CBigNum ret = *this;
|
|
480
|
+
++(*this);
|
|
481
|
+
return ret;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
CBigNum& operator--()
|
|
485
|
+
{
|
|
486
|
+
// prefix operator
|
|
487
|
+
CBigNum r;
|
|
488
|
+
if (!BN_sub(r.get(), self, BN_value_one()))
|
|
489
|
+
throw bignum_error("CBigNum::operator-- : BN_sub failed");
|
|
490
|
+
*this = r;
|
|
491
|
+
return *this;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const CBigNum operator--(int)
|
|
495
|
+
{
|
|
496
|
+
// postfix operator
|
|
497
|
+
const CBigNum ret = *this;
|
|
498
|
+
--(*this);
|
|
499
|
+
return ret;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b);
|
|
504
|
+
friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b);
|
|
505
|
+
friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b);
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
inline const CBigNum operator+(const CBigNum& a, const CBigNum& b)
|
|
511
|
+
{
|
|
512
|
+
CBigNum r;
|
|
513
|
+
if (!BN_add(r.get(), a.cget(), b.cget()))
|
|
514
|
+
throw bignum_error("CBigNum::operator+ : BN_add failed");
|
|
515
|
+
return r;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
inline const CBigNum operator-(const CBigNum& a, const CBigNum& b)
|
|
519
|
+
{
|
|
520
|
+
CBigNum r;
|
|
521
|
+
if (!BN_sub(r.get(), a.cget(), b.cget()))
|
|
522
|
+
throw bignum_error("CBigNum::operator- : BN_sub failed");
|
|
523
|
+
return r;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
inline const CBigNum operator-(const CBigNum& a)
|
|
527
|
+
{
|
|
528
|
+
CBigNum r(a);
|
|
529
|
+
BN_set_negative(r.get(), !BN_is_negative(r.cget()));
|
|
530
|
+
return r;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
inline const CBigNum operator*(const CBigNum& a, const CBigNum& b)
|
|
534
|
+
{
|
|
535
|
+
CAutoBN_CTX pctx;
|
|
536
|
+
CBigNum r;
|
|
537
|
+
if (!BN_mul(r.get(), a.cget(), b.cget(), pctx))
|
|
538
|
+
throw bignum_error("CBigNum::operator* : BN_mul failed");
|
|
539
|
+
return r;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
inline const CBigNum operator/(const CBigNum& a, const CBigNum& b)
|
|
543
|
+
{
|
|
544
|
+
CAutoBN_CTX pctx;
|
|
545
|
+
CBigNum r;
|
|
546
|
+
if (!BN_div(r.get(), NULL, a.cget(), b.cget(), pctx))
|
|
547
|
+
throw bignum_error("CBigNum::operator/ : BN_div failed");
|
|
548
|
+
return r;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
inline const CBigNum operator%(const CBigNum& a, const CBigNum& b)
|
|
552
|
+
{
|
|
553
|
+
CAutoBN_CTX pctx;
|
|
554
|
+
CBigNum r;
|
|
555
|
+
if (!BN_mod(r.get(), a.cget(), b.cget(), pctx))
|
|
556
|
+
throw bignum_error("CBigNum::operator% : BN_div failed");
|
|
557
|
+
return r;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
inline const CBigNum operator<<(const CBigNum& a, unsigned int shift)
|
|
561
|
+
{
|
|
562
|
+
CBigNum r;
|
|
563
|
+
if (!BN_lshift(r.get(), a.cget(), shift))
|
|
564
|
+
throw bignum_error("CBigNum:operator<< : BN_lshift failed");
|
|
565
|
+
return r;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
inline const CBigNum operator>>(const CBigNum& a, unsigned int shift)
|
|
569
|
+
{
|
|
570
|
+
CBigNum r = a;
|
|
571
|
+
r >>= shift;
|
|
572
|
+
return r;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.cget(), b.cget()) == 0); }
|
|
576
|
+
inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.cget(), b.cget()) != 0); }
|
|
577
|
+
inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.cget(), b.cget()) <= 0); }
|
|
578
|
+
inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.cget(), b.cget()) >= 0); }
|
|
579
|
+
inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.cget(), b.cget()) < 0); }
|
|
580
|
+
inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.cget(), b.cget()) > 0); }
|
|
581
|
+
|
|
582
|
+
#endif
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#include <chainparamsbase.h>
|
|
11
11
|
#include <clientversion.h>
|
|
12
|
-
#include <
|
|
12
|
+
#include <consensus/amount.h>
|
|
13
13
|
#include <rpc/client.h>
|
|
14
14
|
#include <rpc/mining.h>
|
|
15
15
|
#include <rpc/protocol.h>
|
|
@@ -138,10 +138,10 @@ static int AppInitRPC(int argc, char* argv[])
|
|
|
138
138
|
strUsage += FormatParagraph(LicenseInfo());
|
|
139
139
|
} else {
|
|
140
140
|
strUsage += "\n"
|
|
141
|
-
"Usage:
|
|
142
|
-
"or:
|
|
143
|
-
"or:
|
|
144
|
-
"or:
|
|
141
|
+
"Usage: peercoin-cli [options] <command> [params] Send command to " PACKAGE_NAME "\n"
|
|
142
|
+
"or: peercoin-cli [options] -named <command> [name=value]... Send command to " PACKAGE_NAME " (with named arguments)\n"
|
|
143
|
+
"or: peercoin-cli [options] help List commands\n"
|
|
144
|
+
"or: peercoin-cli [options] help <command> Get help for a command\n";
|
|
145
145
|
strUsage += "\n" + gArgs.GetHelpMessage();
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -800,7 +800,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
|
|
|
800
800
|
if (response.error != -1) {
|
|
801
801
|
responseErrorMessage = strprintf(" (error code %d - \"%s\")", response.error, http_errorstring(response.error));
|
|
802
802
|
}
|
|
803
|
-
throw CConnectionFailed(strprintf("Could not connect to the server %s:%d%s\n\nMake sure the
|
|
803
|
+
throw CConnectionFailed(strprintf("Could not connect to the server %s:%d%s\n\nMake sure the peercoind server is running and that you are connecting to the correct RPC port.", host, port, responseErrorMessage));
|
|
804
804
|
} else if (response.status == HTTP_UNAUTHORIZED) {
|
|
805
805
|
if (failedToGetAuthCookie) {
|
|
806
806
|
throw std::runtime_error(strprintf(
|
|
@@ -14,14 +14,12 @@
|
|
|
14
14
|
#include <key_io.h>
|
|
15
15
|
#include <fs.h>
|
|
16
16
|
#include <policy/policy.h>
|
|
17
|
-
#include <policy/rbf.h>
|
|
18
17
|
#include <primitives/transaction.h>
|
|
19
18
|
#include <script/script.h>
|
|
20
19
|
#include <script/sign.h>
|
|
21
20
|
#include <script/signingprovider.h>
|
|
22
21
|
#include <univalue.h>
|
|
23
22
|
#include <util/moneystr.h>
|
|
24
|
-
#include <util/rbf.h>
|
|
25
23
|
#include <util/strencodings.h>
|
|
26
24
|
#include <util/string.h>
|
|
27
25
|
#include <util/system.h>
|
|
@@ -54,6 +52,7 @@ static void SetupBitcoinTxArgs(ArgsManager &argsman)
|
|
|
54
52
|
argsman.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", "Add input to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
55
53
|
argsman.AddArg("locktime=N", "Set TX lock time to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
56
54
|
argsman.AddArg("nversion=N", "Set TX version to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
55
|
+
argsman.AddArg("ntime=N", "Set TX timestamp to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
57
56
|
argsman.AddArg("outaddr=VALUE:ADDRESS", "Add address-based output to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
58
57
|
argsman.AddArg("outdata=[VALUE:]DATA", "Add data-based output to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
59
58
|
argsman.AddArg("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", "Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS. "
|
|
@@ -65,7 +64,6 @@ static void SetupBitcoinTxArgs(ArgsManager &argsman)
|
|
|
65
64
|
argsman.AddArg("outscript=VALUE:SCRIPT[:FLAGS]", "Add raw script output to TX. "
|
|
66
65
|
"Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output. "
|
|
67
66
|
"Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
68
|
-
argsman.AddArg("replaceable(=N)", "Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
69
67
|
argsman.AddArg("sign=SIGHASH-FLAGS", "Add zero or more signatures to transaction. "
|
|
70
68
|
"This command requires JSON registers:"
|
|
71
69
|
"prevtxs=JSON object, "
|
|
@@ -101,14 +99,14 @@ static int AppInitRawTx(int argc, char* argv[])
|
|
|
101
99
|
|
|
102
100
|
if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
|
|
103
101
|
// First part of help message is specific to this utility
|
|
104
|
-
std::string strUsage = PACKAGE_NAME "
|
|
102
|
+
std::string strUsage = PACKAGE_NAME " peercoin-tx utility version " + FormatFullVersion() + "\n";
|
|
105
103
|
|
|
106
104
|
if (gArgs.IsArgSet("-version")) {
|
|
107
105
|
strUsage += FormatParagraph(LicenseInfo());
|
|
108
106
|
} else {
|
|
109
107
|
strUsage += "\n"
|
|
110
|
-
"Usage:
|
|
111
|
-
"or:
|
|
108
|
+
"Usage: peercoin-tx [options] <hex-tx> [commands] Update hex-encoded peercoin transaction\n"
|
|
109
|
+
"or: peercoin-tx [options] -create [commands] Create hex-encoded peercoin transaction\n"
|
|
112
110
|
"\n";
|
|
113
111
|
strUsage += gArgs.GetHelpMessage();
|
|
114
112
|
}
|
|
@@ -219,24 +217,13 @@ static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
|
|
|
219
217
|
tx.nLockTime = (unsigned int) newLocktime;
|
|
220
218
|
}
|
|
221
219
|
|
|
222
|
-
static void
|
|
220
|
+
static void MutateTxTime(CMutableTransaction& tx, const std::string& cmdVal)
|
|
223
221
|
{
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
|
|
228
|
-
}
|
|
222
|
+
int64_t newTime;
|
|
223
|
+
if (!ParseInt64(cmdVal, &newTime) || newTime < 0LL || newTime > 0xffffffffLL)
|
|
224
|
+
throw std::runtime_error("Invalid TX time requested: '" + cmdVal + "'");
|
|
229
225
|
|
|
230
|
-
|
|
231
|
-
int cnt = 0;
|
|
232
|
-
for (CTxIn& txin : tx.vin) {
|
|
233
|
-
if (strInIdx == "" || cnt == inIdx) {
|
|
234
|
-
if (txin.nSequence > MAX_BIP125_RBF_SEQUENCE) {
|
|
235
|
-
txin.nSequence = MAX_BIP125_RBF_SEQUENCE;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
++cnt;
|
|
239
|
-
}
|
|
226
|
+
tx.nTime = (unsigned int) newTime;
|
|
240
227
|
}
|
|
241
228
|
|
|
242
229
|
template <typename T>
|
|
@@ -559,7 +546,7 @@ static CAmount AmountFromValue(const UniValue& value)
|
|
|
559
546
|
if (!value.isNum() && !value.isStr())
|
|
560
547
|
throw std::runtime_error("Amount is not a number or string");
|
|
561
548
|
CAmount amount;
|
|
562
|
-
if (!ParseFixedPoint(value.getValStr(),
|
|
549
|
+
if (!ParseFixedPoint(value.getValStr(), 6, &amount))
|
|
563
550
|
throw std::runtime_error("Invalid amount");
|
|
564
551
|
if (!MoneyRange(amount))
|
|
565
552
|
throw std::runtime_error("Amount out of range");
|
|
@@ -708,9 +695,8 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
|
|
|
708
695
|
MutateTxVersion(tx, commandVal);
|
|
709
696
|
else if (command == "locktime")
|
|
710
697
|
MutateTxLocktime(tx, commandVal);
|
|
711
|
-
else if (command == "
|
|
712
|
-
|
|
713
|
-
}
|
|
698
|
+
else if (command == "ntime")
|
|
699
|
+
MutateTxTime(tx, commandVal);
|
|
714
700
|
|
|
715
701
|
else if (command == "delin")
|
|
716
702
|
MutateTxDelInput(tx, commandVal);
|
|
@@ -114,7 +114,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
|
|
|
114
114
|
|
|
115
115
|
util::ThreadSetInternalName("init");
|
|
116
116
|
|
|
117
|
-
// If Qt is used, parameters/
|
|
117
|
+
// If Qt is used, parameters/peercoin.conf are parsed in qt/bitcoin.cpp's main()
|
|
118
118
|
ArgsManager& args = *Assert(node.args);
|
|
119
119
|
SetupServerArgs(args);
|
|
120
120
|
std::string error;
|
|
@@ -129,7 +129,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
|
|
|
129
129
|
if (args.IsArgSet("-version")) {
|
|
130
130
|
strUsage += FormatParagraph(LicenseInfo());
|
|
131
131
|
} else {
|
|
132
|
-
strUsage += "\nUsage:
|
|
132
|
+
strUsage += "\nUsage: peercoind [options] Start " PACKAGE_NAME "\n"
|
|
133
133
|
"\n";
|
|
134
134
|
strUsage += args.GetHelpMessage();
|
|
135
135
|
}
|
|
@@ -165,7 +165,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
|
|
|
165
165
|
// Error out when loose non-argument tokens are encountered on command line
|
|
166
166
|
for (int i = 1; i < argc; i++) {
|
|
167
167
|
if (!IsSwitchChar(argv[i][0])) {
|
|
168
|
-
return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see
|
|
168
|
+
return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see peercoind -h for a list of options.\n", argv[i])));
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
|
|
19
19
|
CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, bool fUseWTXID) :
|
|
20
20
|
nonce(GetRand(std::numeric_limits<uint64_t>::max())),
|
|
21
|
-
shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) {
|
|
21
|
+
shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block), vchBlockSig(block.vchBlockSig) {
|
|
22
22
|
FillShortTxIDSelector();
|
|
23
23
|
//TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase
|
|
24
24
|
prefilledtxn[0] = {0, block.vtx[0]};
|
|
25
|
+
header.nFlags = block.nFlags;
|
|
25
26
|
for (size_t i = 1; i < block.vtx.size(); i++) {
|
|
26
27
|
const CTransaction& tx = *block.vtx[i];
|
|
27
28
|
shorttxids[i - 1] = GetShortID(fUseWTXID ? tx.GetWitnessHash() : tx.GetHash());
|
|
@@ -54,6 +55,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
|
|
|
54
55
|
|
|
55
56
|
assert(header.IsNull() && txn_available.empty());
|
|
56
57
|
header = cmpctblock.header;
|
|
58
|
+
vchBlockSig = cmpctblock.vchBlockSig;
|
|
57
59
|
txn_available.resize(cmpctblock.BlockTxCount());
|
|
58
60
|
|
|
59
61
|
int32_t lastprefilledindex = -1;
|
|
@@ -177,6 +179,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
|
|
|
177
179
|
assert(!header.IsNull());
|
|
178
180
|
uint256 hash = header.GetHash();
|
|
179
181
|
block = header;
|
|
182
|
+
block.vchBlockSig = vchBlockSig;
|
|
180
183
|
block.vtx.resize(txn_available.size());
|
|
181
184
|
|
|
182
185
|
size_t tx_missing_offset = 0;
|
|
@@ -100,6 +100,7 @@ public:
|
|
|
100
100
|
static constexpr int SHORTTXIDS_LENGTH = 6;
|
|
101
101
|
|
|
102
102
|
CBlockHeader header;
|
|
103
|
+
std::vector<unsigned char> vchBlockSig;
|
|
103
104
|
|
|
104
105
|
// Dummy for deserialization
|
|
105
106
|
CBlockHeaderAndShortTxIDs() {}
|
|
@@ -112,7 +113,7 @@ public:
|
|
|
112
113
|
|
|
113
114
|
SERIALIZE_METHODS(CBlockHeaderAndShortTxIDs, obj)
|
|
114
115
|
{
|
|
115
|
-
READWRITE(obj.header, obj.nonce, Using<VectorFormatter<CustomUintFormatter<SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn);
|
|
116
|
+
READWRITE(obj.header, obj.nonce, obj.vchBlockSig, Using<VectorFormatter<CustomUintFormatter<SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn);
|
|
116
117
|
if (ser_action.ForRead()) {
|
|
117
118
|
if (obj.BlockTxCount() > std::numeric_limits<uint16_t>::max()) {
|
|
118
119
|
throw std::ios_base::failure("indexes overflowed 16 bits");
|
|
@@ -129,6 +130,7 @@ protected:
|
|
|
129
130
|
const CTxMemPool* pool;
|
|
130
131
|
public:
|
|
131
132
|
CBlockHeader header;
|
|
133
|
+
std::vector<unsigned char> vchBlockSig;
|
|
132
134
|
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
|
|
133
135
|
|
|
134
136
|
// extra_txn is a list of extra transactions to look at, in <witness hash, reference> form
|
|
@@ -122,7 +122,7 @@ void CBlockIndex::BuildSkip()
|
|
|
122
122
|
pskip = pprev->GetAncestor(GetSkipHeight(nHeight));
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
arith_uint256
|
|
125
|
+
arith_uint256 GetBlockTrust(const CBlockIndex& block)
|
|
126
126
|
{
|
|
127
127
|
arith_uint256 bnTarget;
|
|
128
128
|
bool fNegative;
|
|
@@ -134,20 +134,20 @@ arith_uint256 GetBlockProof(const CBlockIndex& block)
|
|
|
134
134
|
// as it's too large for an arith_uint256. However, as 2**256 is at least as large
|
|
135
135
|
// as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) / (bnTarget+1)) + 1,
|
|
136
136
|
// or ~bnTarget / (bnTarget+1) + 1.
|
|
137
|
-
return (~bnTarget / (bnTarget + 1)) + 1;
|
|
137
|
+
return block.IsProofOfStake() ? (~bnTarget / (bnTarget + 1)) + 1 : 1;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params& params)
|
|
141
141
|
{
|
|
142
142
|
arith_uint256 r;
|
|
143
143
|
int sign = 1;
|
|
144
|
-
if (to.
|
|
145
|
-
r = to.
|
|
144
|
+
if (to.nChainTrust > from.nChainTrust) {
|
|
145
|
+
r = to.nChainTrust - from.nChainTrust;
|
|
146
146
|
} else {
|
|
147
|
-
r = from.
|
|
147
|
+
r = from.nChainTrust - to.nChainTrust;
|
|
148
148
|
sign = -1;
|
|
149
149
|
}
|
|
150
|
-
r = r * arith_uint256(params.nPowTargetSpacing) /
|
|
150
|
+
r = r * arith_uint256(params.nPowTargetSpacing) / GetBlockTrust(tip);
|
|
151
151
|
if (r.bits() > 63) {
|
|
152
152
|
return sign * std::numeric_limits<int64_t>::max();
|
|
153
153
|
}
|
|
@@ -172,3 +172,11 @@ const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex*
|
|
|
172
172
|
assert(pa == pb);
|
|
173
173
|
return pa;
|
|
174
174
|
}
|
|
175
|
+
|
|
176
|
+
// peercoin: find last block index up to pindex
|
|
177
|
+
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake)
|
|
178
|
+
{
|
|
179
|
+
while (pindex && pindex->pprev && (pindex->IsProofOfStake() != fProofOfStake))
|
|
180
|
+
pindex = pindex->pprev;
|
|
181
|
+
return pindex;
|
|
182
|
+
}
|
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
#include <tinyformat.h>
|
|
15
15
|
#include <uint256.h>
|
|
16
16
|
|
|
17
|
+
#include <util/moneystr.h>
|
|
18
|
+
|
|
17
19
|
#include <vector>
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Maximum amount of time that a block timestamp is allowed to exceed the
|
|
21
23
|
* current network-adjusted time before the block will be accepted.
|
|
22
24
|
*/
|
|
23
|
-
static constexpr int64_t
|
|
25
|
+
static constexpr int64_t MAX_FUTURE_BLOCK_TIME_PREV9 = 2 * 60 * 60;
|
|
26
|
+
static constexpr int64_t MAX_FUTURE_BLOCK_TIME = 15 * 60;
|
|
24
27
|
|
|
25
28
|
/**
|
|
26
29
|
* Timestamp window used as a grace period by code that compares external
|
|
@@ -28,7 +31,7 @@ static constexpr int64_t MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60;
|
|
|
28
31
|
* to block timestamps. This should be set at least as high as
|
|
29
32
|
* MAX_FUTURE_BLOCK_TIME.
|
|
30
33
|
*/
|
|
31
|
-
static constexpr int64_t TIMESTAMP_WINDOW =
|
|
34
|
+
static constexpr int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME_PREV9;
|
|
32
35
|
|
|
33
36
|
/**
|
|
34
37
|
* Maximum gap between node time and block time used
|
|
@@ -173,7 +176,7 @@ public:
|
|
|
173
176
|
unsigned int nUndoPos GUARDED_BY(::cs_main){0};
|
|
174
177
|
|
|
175
178
|
//! (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block
|
|
176
|
-
arith_uint256
|
|
179
|
+
arith_uint256 nChainTrust{};
|
|
177
180
|
|
|
178
181
|
//! Number of transactions in this block.
|
|
179
182
|
//! Note: in a potential headers-first mode, this number cannot be relied upon
|
|
@@ -213,6 +216,66 @@ public:
|
|
|
213
216
|
//! (memory only) Maximum nTime in the chain up to and including this block.
|
|
214
217
|
unsigned int nTimeMax{0};
|
|
215
218
|
|
|
219
|
+
// peercoin
|
|
220
|
+
// peercoin: money supply related block index fields
|
|
221
|
+
int64_t nMint{0};
|
|
222
|
+
int64_t nMoneySupply{0};
|
|
223
|
+
|
|
224
|
+
// peercoin: proof-of-stake related block index fields
|
|
225
|
+
unsigned int nFlags{0}; // peercoin: block index flags
|
|
226
|
+
enum
|
|
227
|
+
{
|
|
228
|
+
BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block
|
|
229
|
+
BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier
|
|
230
|
+
BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier
|
|
231
|
+
};
|
|
232
|
+
uint64_t nStakeModifier{0}; // hash modifier for proof-of-stake
|
|
233
|
+
unsigned int nStakeModifierChecksum{0}; // checksum of index; in-memeory only
|
|
234
|
+
COutPoint prevoutStake{};
|
|
235
|
+
unsigned int nStakeTime{0};
|
|
236
|
+
uint256 hashProofOfStake{};
|
|
237
|
+
|
|
238
|
+
bool IsProofOfWork() const
|
|
239
|
+
{
|
|
240
|
+
return !(nFlags & BLOCK_PROOF_OF_STAKE);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
bool IsProofOfStake() const
|
|
244
|
+
{
|
|
245
|
+
return (nFlags & BLOCK_PROOF_OF_STAKE);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
void SetProofOfStake()
|
|
249
|
+
{
|
|
250
|
+
nFlags |= BLOCK_PROOF_OF_STAKE;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
unsigned int GetStakeEntropyBit() const
|
|
254
|
+
{
|
|
255
|
+
return ((nFlags & BLOCK_STAKE_ENTROPY) >> 1);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
bool SetStakeEntropyBit(unsigned int nEntropyBit)
|
|
259
|
+
{
|
|
260
|
+
if (nEntropyBit > 1)
|
|
261
|
+
return false;
|
|
262
|
+
nFlags |= (nEntropyBit? BLOCK_STAKE_ENTROPY : 0);
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
bool GeneratedStakeModifier() const
|
|
267
|
+
{
|
|
268
|
+
return (nFlags & BLOCK_STAKE_MODIFIER);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
void SetStakeModifier(uint64_t nModifier, bool fGeneratedStakeModifier)
|
|
272
|
+
{
|
|
273
|
+
nStakeModifier = nModifier;
|
|
274
|
+
if (fGeneratedStakeModifier)
|
|
275
|
+
nFlags |= BLOCK_STAKE_MODIFIER;
|
|
276
|
+
}
|
|
277
|
+
// peercoin end
|
|
278
|
+
|
|
216
279
|
CBlockIndex()
|
|
217
280
|
{
|
|
218
281
|
}
|
|
@@ -222,7 +285,8 @@ public:
|
|
|
222
285
|
hashMerkleRoot{block.hashMerkleRoot},
|
|
223
286
|
nTime{block.nTime},
|
|
224
287
|
nBits{block.nBits},
|
|
225
|
-
nNonce{block.nNonce}
|
|
288
|
+
nNonce{block.nNonce},
|
|
289
|
+
nFlags{block.nFlags}
|
|
226
290
|
{
|
|
227
291
|
}
|
|
228
292
|
|
|
@@ -258,6 +322,7 @@ public:
|
|
|
258
322
|
block.nTime = nTime;
|
|
259
323
|
block.nBits = nBits;
|
|
260
324
|
block.nNonce = nNonce;
|
|
325
|
+
block.nFlags = nFlags;
|
|
261
326
|
return block;
|
|
262
327
|
}
|
|
263
328
|
|
|
@@ -271,7 +336,7 @@ public:
|
|
|
271
336
|
* downloaded (and stored to disk) at some point.
|
|
272
337
|
*
|
|
273
338
|
* Does not imply the transactions are consensus-valid (ConnectTip might fail)
|
|
274
|
-
* Does not imply the transactions are still stored on disk.
|
|
339
|
+
* Does not imply the transactions are still stored on disk.
|
|
275
340
|
*/
|
|
276
341
|
bool HaveTxsDownloaded() const { return nChainTx != 0; }
|
|
277
342
|
|
|
@@ -285,6 +350,32 @@ public:
|
|
|
285
350
|
return (int64_t)nTimeMax;
|
|
286
351
|
}
|
|
287
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Duplicate from bitcoinrpc that originaly define this method.
|
|
355
|
+
* May require some cleanup since this method should be available both for rpc
|
|
356
|
+
* and qt clients.
|
|
357
|
+
*/
|
|
358
|
+
double GetBlockDifficulty() const
|
|
359
|
+
{
|
|
360
|
+
int nShift = (nBits >> 24) & 0xff;
|
|
361
|
+
|
|
362
|
+
double dDiff =
|
|
363
|
+
(double)0x0000ffff / (double)(nBits & 0x00ffffff);
|
|
364
|
+
|
|
365
|
+
while (nShift < 29)
|
|
366
|
+
{
|
|
367
|
+
dDiff *= 256.0;
|
|
368
|
+
nShift++;
|
|
369
|
+
}
|
|
370
|
+
while (nShift > 29)
|
|
371
|
+
{
|
|
372
|
+
dDiff /= 256.0;
|
|
373
|
+
nShift--;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return dDiff;
|
|
377
|
+
}
|
|
378
|
+
|
|
288
379
|
static constexpr int nMedianTimeSpan = 11;
|
|
289
380
|
|
|
290
381
|
int64_t GetMedianTimePast() const
|
|
@@ -303,10 +394,15 @@ public:
|
|
|
303
394
|
|
|
304
395
|
std::string ToString() const
|
|
305
396
|
{
|
|
306
|
-
return strprintf("CBlockIndex(
|
|
307
|
-
pprev, nHeight,
|
|
308
|
-
|
|
309
|
-
|
|
397
|
+
return strprintf("CBlockIndex(nprev=%08x, nFile=%d, nHeight=%d, nMint=%s, nMoneySupply=%s, nFlags=(%s)(%d)(%s), nStakeModifier=%016llx, nStakeModifierChecksum=%08x, hashProofOfStake=%s, prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)",
|
|
398
|
+
pprev, nFile, nHeight,
|
|
399
|
+
FormatMoney(nMint), FormatMoney(nMoneySupply),
|
|
400
|
+
GeneratedStakeModifier() ? "MOD" : "-", GetStakeEntropyBit(), IsProofOfStake()? "PoS" : "PoW",
|
|
401
|
+
nStakeModifier, nStakeModifierChecksum,
|
|
402
|
+
hashProofOfStake.ToString(),
|
|
403
|
+
prevoutStake.ToString(), nStakeTime,
|
|
404
|
+
hashMerkleRoot.ToString().substr(0,10),
|
|
405
|
+
GetBlockHash().ToString().substr(0,20));
|
|
310
406
|
}
|
|
311
407
|
|
|
312
408
|
//! Check whether this block index entry is valid up to the passed validity level.
|
|
@@ -357,7 +453,7 @@ public:
|
|
|
357
453
|
const CBlockIndex* GetAncestor(int height) const;
|
|
358
454
|
};
|
|
359
455
|
|
|
360
|
-
arith_uint256
|
|
456
|
+
arith_uint256 GetBlockTrust(const CBlockIndex& block);
|
|
361
457
|
/** Return the time it would take to redo the work difference between from and to, assuming the current hashrate corresponds to the difficulty at tip, in seconds. */
|
|
362
458
|
int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params&);
|
|
363
459
|
/** Find the forking point between two chain tips. */
|
|
@@ -393,6 +489,17 @@ public:
|
|
|
393
489
|
if (obj.nStatus & BLOCK_HAVE_DATA) READWRITE(VARINT(obj.nDataPos));
|
|
394
490
|
if (obj.nStatus & BLOCK_HAVE_UNDO) READWRITE(VARINT(obj.nUndoPos));
|
|
395
491
|
|
|
492
|
+
READWRITE(obj.nMint);
|
|
493
|
+
READWRITE(obj.nMoneySupply);
|
|
494
|
+
READWRITE(obj.nFlags);
|
|
495
|
+
READWRITE(obj.nStakeModifier);
|
|
496
|
+
if (obj.nFlags & BLOCK_PROOF_OF_STAKE)
|
|
497
|
+
{
|
|
498
|
+
READWRITE(obj.prevoutStake);
|
|
499
|
+
READWRITE(obj.nStakeTime);
|
|
500
|
+
READWRITE(obj.hashProofOfStake);
|
|
501
|
+
}
|
|
502
|
+
|
|
396
503
|
// block header
|
|
397
504
|
READWRITE(obj.nVersion);
|
|
398
505
|
READWRITE(obj.hashPrev);
|
|
@@ -491,4 +598,6 @@ public:
|
|
|
491
598
|
CBlockIndex* FindEarliestAtLeast(int64_t nTime, int height) const;
|
|
492
599
|
};
|
|
493
600
|
|
|
601
|
+
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake);
|
|
602
|
+
|
|
494
603
|
#endif // BITCOIN_CHAIN_H
|
|
@@ -16,18 +16,19 @@
|
|
|
16
16
|
#include <boost/algorithm/string/classification.hpp>
|
|
17
17
|
#include <boost/algorithm/string/split.hpp>
|
|
18
18
|
|
|
19
|
-
static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t
|
|
19
|
+
static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTimeTx, uint32_t nTimeBlock, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
|
|
20
20
|
{
|
|
21
21
|
CMutableTransaction txNew;
|
|
22
22
|
txNew.nVersion = 1;
|
|
23
23
|
txNew.vin.resize(1);
|
|
24
24
|
txNew.vout.resize(1);
|
|
25
|
-
txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(
|
|
25
|
+
txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(9999) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
|
|
26
26
|
txNew.vout[0].nValue = genesisReward;
|
|
27
27
|
txNew.vout[0].scriptPubKey = genesisOutputScript;
|
|
28
|
+
txNew.nTime = nTimeTx;
|
|
28
29
|
|
|
29
30
|
CBlock genesis;
|
|
30
|
-
genesis.nTime =
|
|
31
|
+
genesis.nTime = nTimeBlock;
|
|
31
32
|
genesis.nBits = nBits;
|
|
32
33
|
genesis.nNonce = nNonce;
|
|
33
34
|
genesis.nVersion = nVersion;
|
|
@@ -48,11 +49,11 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesi
|
|
|
48
49
|
* CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
|
|
49
50
|
* vMerkleTree: 4a5e1e
|
|
50
51
|
*/
|
|
51
|
-
static CBlock CreateGenesisBlock(uint32_t
|
|
52
|
+
static CBlock CreateGenesisBlock(uint32_t nTimeTx, uint32_t nTimeBlock, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
|
|
52
53
|
{
|
|
53
|
-
const char* pszTimestamp = "
|
|
54
|
-
const CScript genesisOutputScript = CScript()
|
|
55
|
-
return CreateGenesisBlock(pszTimestamp, genesisOutputScript,
|
|
54
|
+
const char* pszTimestamp = "Matonis 07-AUG-2012 Parallel Currencies And The Roadmap To Monetary Freedom";
|
|
55
|
+
const CScript genesisOutputScript = CScript();
|
|
56
|
+
return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTimeTx, nTimeBlock, nNonce, nBits, nVersion, genesisReward);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
/**
|
|
@@ -64,80 +65,71 @@ public:
|
|
|
64
65
|
strNetworkID = CBaseChainParams::MAIN;
|
|
65
66
|
consensus.signet_blocks = false;
|
|
66
67
|
consensus.signet_challenge.clear();
|
|
67
|
-
consensus.
|
|
68
|
-
consensus.
|
|
69
|
-
consensus.
|
|
70
|
-
consensus.
|
|
71
|
-
consensus.
|
|
72
|
-
|
|
73
|
-
consensus.
|
|
74
|
-
consensus.
|
|
75
|
-
consensus.
|
|
76
|
-
consensus.
|
|
77
|
-
consensus.
|
|
78
|
-
consensus.
|
|
68
|
+
//consensus.BIP16Height = 0;
|
|
69
|
+
consensus.BIP34Height = 339994;
|
|
70
|
+
consensus.BIP34Hash = uint256S("000000000000000237f50af4cfe8924e8693abc5bd8ae5abb95bc6d230f5953f");
|
|
71
|
+
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 32;
|
|
72
|
+
consensus.bnInitialHashTarget = uint256S("0000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 40;
|
|
73
|
+
|
|
74
|
+
consensus.nTargetTimespan = 7 * 24 * 60 * 60; // one week
|
|
75
|
+
consensus.nStakeTargetSpacing = 10 * 60; // 10-minute block spacing
|
|
76
|
+
consensus.nTargetSpacingWorkMax = 12 * consensus.nStakeTargetSpacing; // 2-hour
|
|
77
|
+
consensus.nPowTargetSpacing = consensus.nStakeTargetSpacing;
|
|
78
|
+
consensus.nStakeMinAge = 60 * 60 * 24 * 30; // minimum age for coin age
|
|
79
|
+
consensus.nStakeMaxAge = 60 * 60 * 24 * 90;
|
|
80
|
+
consensus.nModifierInterval = 6 * 60 * 60; // Modifier interval: time to elapse before new modifier is computed
|
|
81
|
+
consensus.nCoinbaseMaturity = 500;
|
|
82
|
+
|
|
79
83
|
consensus.fPowAllowMinDifficultyBlocks = false;
|
|
80
84
|
consensus.fPowNoRetargeting = false;
|
|
81
85
|
consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
|
|
82
86
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
|
83
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
|
84
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
|
85
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
86
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
|
90
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021
|
|
91
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
|
|
92
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021
|
|
88
|
+
consensus.SegwitHeight = 455470;
|
|
93
89
|
|
|
94
|
-
consensus.nMinimumChainWork = uint256S("
|
|
95
|
-
consensus.defaultAssumeValid = uint256S("
|
|
90
|
+
consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000002a0fac8b39f476"); // 350000
|
|
91
|
+
consensus.defaultAssumeValid = uint256S("0xa3a0ffa0dbca75923ad6a53d3878d62f8b35c363282df3f13ded9e4fda921e63"); // 380000
|
|
96
92
|
|
|
97
93
|
/**
|
|
98
94
|
* The message start string is designed to be unlikely to occur in normal data.
|
|
99
95
|
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
|
|
100
96
|
* a large 32-bit integer with any alignment.
|
|
101
97
|
*/
|
|
102
|
-
pchMessageStart[0] =
|
|
103
|
-
pchMessageStart[1] =
|
|
104
|
-
pchMessageStart[2] =
|
|
105
|
-
pchMessageStart[3] =
|
|
106
|
-
nDefaultPort =
|
|
98
|
+
pchMessageStart[0] = 0xe6;
|
|
99
|
+
pchMessageStart[1] = 0xe8;
|
|
100
|
+
pchMessageStart[2] = 0xe9;
|
|
101
|
+
pchMessageStart[3] = 0xe5;
|
|
102
|
+
nDefaultPort = 9901;
|
|
107
103
|
nPruneAfterHeight = 100000;
|
|
108
|
-
m_assumed_blockchain_size =
|
|
109
|
-
m_assumed_chain_state_size = 6;
|
|
104
|
+
m_assumed_blockchain_size = 1;
|
|
110
105
|
|
|
111
|
-
genesis = CreateGenesisBlock(
|
|
106
|
+
genesis = CreateGenesisBlock(1345083810, 1345084287, 2179302059u, 0x1d00ffff, 1, 0);
|
|
112
107
|
consensus.hashGenesisBlock = genesis.GetHash();
|
|
113
|
-
assert(consensus.hashGenesisBlock == uint256S("
|
|
114
|
-
assert(genesis.hashMerkleRoot == uint256S("
|
|
108
|
+
assert(consensus.hashGenesisBlock == uint256S("0x0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3"));
|
|
109
|
+
assert(genesis.hashMerkleRoot == uint256S("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2"));
|
|
115
110
|
|
|
116
111
|
// Note that of those which support the service bits prefix, most only support a subset of
|
|
117
112
|
// possible options.
|
|
118
113
|
// This is fine at runtime as we'll fall back to using them as an addrfetch if they don't support the
|
|
119
114
|
// service bits we want, but we should get them updated to support all service bits wanted by any
|
|
120
115
|
// release ASAP to avoid it where possible.
|
|
121
|
-
vSeeds.emplace_back("seed.
|
|
122
|
-
vSeeds.emplace_back("
|
|
123
|
-
vSeeds.emplace_back("
|
|
124
|
-
vSeeds.emplace_back("seed.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
vSeeds.emplace_back("seed.bitcoin.wiz.biz."); // Jason Maurice
|
|
130
|
-
|
|
131
|
-
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0);
|
|
132
|
-
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5);
|
|
133
|
-
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,128);
|
|
116
|
+
vSeeds.emplace_back("seed.peercoin.net");
|
|
117
|
+
vSeeds.emplace_back("seed2.peercoin.net");
|
|
118
|
+
vSeeds.emplace_back("seed.peercoin-library.org");
|
|
119
|
+
vSeeds.emplace_back("seed.ppcoin.info");
|
|
120
|
+
|
|
121
|
+
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,55); // peercoin: addresses begin with 'P'
|
|
122
|
+
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,117); // peercoin: addresses begin with 'p'
|
|
123
|
+
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,183);
|
|
134
124
|
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E};
|
|
135
125
|
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
|
|
136
126
|
|
|
137
|
-
|
|
127
|
+
// human readable prefix to bench32 address
|
|
128
|
+
bech32_hrp = "pc";
|
|
138
129
|
|
|
139
130
|
vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_main), std::end(chainparams_seed_main));
|
|
140
131
|
|
|
132
|
+
fMiningRequiresPeers = true;
|
|
141
133
|
fDefaultConsistencyChecks = false;
|
|
142
134
|
fRequireStandard = true;
|
|
143
135
|
m_is_test_chain = false;
|
|
@@ -145,19 +137,19 @@ public:
|
|
|
145
137
|
|
|
146
138
|
checkpointData = {
|
|
147
139
|
{
|
|
148
|
-
{
|
|
149
|
-
{
|
|
150
|
-
{
|
|
151
|
-
{
|
|
152
|
-
{
|
|
153
|
-
{
|
|
154
|
-
{
|
|
155
|
-
{
|
|
156
|
-
{
|
|
157
|
-
{
|
|
158
|
-
{
|
|
159
|
-
{
|
|
160
|
-
{
|
|
140
|
+
{ 0, uint256S("0x0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3")},
|
|
141
|
+
{ 19080, uint256S("0x000000000000bca54d9ac17881f94193fd6a270c1bb21c3bf0b37f588a40dbd7")},
|
|
142
|
+
{ 30583, uint256S("0xd39d1481a7eecba48932ea5913be58ad3894c7ee6d5a8ba8abeb772c66a6696e")},
|
|
143
|
+
{ 99999, uint256S("0x27fd5e1de16a4270eb8c68dee2754a64da6312c7c3a0e99a7e6776246be1ee3f")},
|
|
144
|
+
{219999, uint256S("0xab0dad4b10d2370f009ed6df6effca1ba42f01d5070d6b30afeedf6463fbe7a2")},
|
|
145
|
+
{336000, uint256S("0x4d261cef6e61a5ed8325e560f1d6e36f4698853a4c7134677f47a1d1d842bdf6")},
|
|
146
|
+
{371850, uint256S("0x6b18adcb0a6e080dae85b74eee2b83fabb157bbea64fab0ed2192b2f6d5b89f3")},
|
|
147
|
+
{407813, uint256S("0x00000000000000012730b0f48bed8afbeb08164c9d63597afb082e82ea05cec9")},
|
|
148
|
+
{443561, uint256S("0xf81cea8e4e40b2cfcc13a8bd82436399c35a55df951b95e7128601c1838029ed")},
|
|
149
|
+
{455470, uint256S("0xd1472c26229f90b8589d331aa47ba9023cb953b92dce342c753e7a6b3431bf1e")},
|
|
150
|
+
{479189, uint256S("0xc9c065028b20a23fbb9627bbca5946c7497f11e1f72433d4d215c79047cf06f2")},
|
|
151
|
+
{504051, uint256S("0xff65454ebdf1d89174bec10a3c016db92f7b1d9a4759603472842f254be8d7b3")},
|
|
152
|
+
{589659, uint256S("0x967c14abf21214639aeff0a270c4543cd3b80fe53178384ac5aa3c277662f1d0")},
|
|
161
153
|
}
|
|
162
154
|
};
|
|
163
155
|
|
|
@@ -166,10 +158,12 @@ public:
|
|
|
166
158
|
};
|
|
167
159
|
|
|
168
160
|
chainTxData = ChainTxData{
|
|
169
|
-
// Data
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
161
|
+
// Data as of block 967c14abf21214639aeff0a270c4543cd3b80fe53178384ac5aa3c277662f1d0 (height 589659).
|
|
162
|
+
1635782211, // * UNIX timestamp of last known number of transactions
|
|
163
|
+
1992832, // * total number of transactions between genesis and that timestamp
|
|
164
|
+
// (the tx=... number in the ChainStateFlushed debug.log lines)
|
|
165
|
+
0.006862798 // * estimated number of transactions per second after that timestamp
|
|
166
|
+
// 1992832/(1635782211-1345400356) = 0.006862798
|
|
173
167
|
};
|
|
174
168
|
}
|
|
175
169
|
};
|
|
@@ -183,57 +177,51 @@ public:
|
|
|
183
177
|
strNetworkID = CBaseChainParams::TESTNET;
|
|
184
178
|
consensus.signet_blocks = false;
|
|
185
179
|
consensus.signet_challenge.clear();
|
|
186
|
-
consensus.
|
|
187
|
-
consensus.
|
|
188
|
-
consensus.
|
|
189
|
-
consensus.
|
|
190
|
-
consensus.
|
|
191
|
-
|
|
192
|
-
consensus.
|
|
193
|
-
consensus.
|
|
194
|
-
consensus.
|
|
195
|
-
consensus.
|
|
196
|
-
consensus.
|
|
197
|
-
consensus.
|
|
180
|
+
//consensus.BIP16Height = 0;
|
|
181
|
+
consensus.BIP34Height = 293368;
|
|
182
|
+
consensus.BIP34Hash = uint256S("00000002c0b976c7a5c9878f1cec63fb4d88d68d614aedeaf8158c42d904795e");
|
|
183
|
+
consensus.powLimit = uint256S("0000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 28;
|
|
184
|
+
consensus.bnInitialHashTarget = uint256S("00000007ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 29;
|
|
185
|
+
|
|
186
|
+
consensus.nTargetTimespan = 7 * 24 * 60 * 60; // one week
|
|
187
|
+
consensus.nStakeTargetSpacing = 10 * 60; // 10-minute block spacing
|
|
188
|
+
consensus.nTargetSpacingWorkMax = 12 * consensus.nStakeTargetSpacing; // 2-hour
|
|
189
|
+
consensus.nPowTargetSpacing = consensus.nStakeTargetSpacing;
|
|
190
|
+
consensus.nStakeMinAge = 60 * 60 * 24; // test net min age is 1 day
|
|
191
|
+
consensus.nStakeMaxAge = 60 * 60 * 24 * 90;
|
|
192
|
+
consensus.nModifierInterval = 60 * 20; // Modifier interval: time to elapse before new modifier is computed
|
|
193
|
+
consensus.nCoinbaseMaturity = 60;
|
|
194
|
+
|
|
198
195
|
consensus.fPowAllowMinDifficultyBlocks = true;
|
|
199
196
|
consensus.fPowNoRetargeting = false;
|
|
200
197
|
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
|
|
201
198
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
|
202
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
|
203
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
|
204
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
205
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
|
|
206
199
|
|
|
207
|
-
|
|
208
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
|
209
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021
|
|
210
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
|
|
211
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
|
|
200
|
+
consensus.SegwitHeight = 394215;
|
|
212
201
|
|
|
213
|
-
consensus.nMinimumChainWork = uint256S("
|
|
214
|
-
consensus.defaultAssumeValid = uint256S("
|
|
202
|
+
consensus.nMinimumChainWork = uint256S("0x00");
|
|
203
|
+
consensus.defaultAssumeValid = uint256S("0x0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1"); //1135275
|
|
215
204
|
|
|
216
|
-
pchMessageStart[0] =
|
|
217
|
-
pchMessageStart[1] =
|
|
218
|
-
pchMessageStart[2] =
|
|
219
|
-
pchMessageStart[3] =
|
|
220
|
-
nDefaultPort =
|
|
205
|
+
pchMessageStart[0] = 0xcb;
|
|
206
|
+
pchMessageStart[1] = 0xf2;
|
|
207
|
+
pchMessageStart[2] = 0xc0;
|
|
208
|
+
pchMessageStart[3] = 0xef;
|
|
209
|
+
nDefaultPort = 9903;
|
|
221
210
|
nPruneAfterHeight = 1000;
|
|
222
|
-
m_assumed_blockchain_size =
|
|
223
|
-
m_assumed_chain_state_size = 2;
|
|
211
|
+
m_assumed_blockchain_size = 2;
|
|
224
212
|
|
|
225
|
-
genesis = CreateGenesisBlock(
|
|
213
|
+
genesis = CreateGenesisBlock(1345083810, 1345090000, 122894938, 0x1d0fffff, 1, 0);
|
|
226
214
|
consensus.hashGenesisBlock = genesis.GetHash();
|
|
227
|
-
assert(consensus.hashGenesisBlock == uint256S("
|
|
228
|
-
assert(genesis.hashMerkleRoot == uint256S("
|
|
215
|
+
assert(consensus.hashGenesisBlock == uint256S("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06"));
|
|
216
|
+
assert(genesis.hashMerkleRoot == uint256S("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2"));
|
|
229
217
|
|
|
230
218
|
vFixedSeeds.clear();
|
|
231
219
|
vSeeds.clear();
|
|
232
220
|
// nodes with support for servicebits filtering should be at the top
|
|
233
|
-
vSeeds.emplace_back("
|
|
234
|
-
vSeeds.emplace_back("
|
|
235
|
-
vSeeds.emplace_back("
|
|
236
|
-
vSeeds.emplace_back("
|
|
221
|
+
vSeeds.emplace_back("tseed.peercoin.net");
|
|
222
|
+
vSeeds.emplace_back("tseed2.peercoin.net");
|
|
223
|
+
vSeeds.emplace_back("tseed.peercoin-library.org");
|
|
224
|
+
vSeeds.emplace_back("testseed.ppcoin.info");
|
|
237
225
|
|
|
238
226
|
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111);
|
|
239
227
|
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
|
|
@@ -241,10 +229,12 @@ public:
|
|
|
241
229
|
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
|
242
230
|
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
|
243
231
|
|
|
244
|
-
|
|
232
|
+
// human readable prefix to bench32 address
|
|
233
|
+
bech32_hrp = "tpc";
|
|
245
234
|
|
|
246
235
|
vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_test), std::end(chainparams_seed_test));
|
|
247
236
|
|
|
237
|
+
fMiningRequiresPeers = true;
|
|
248
238
|
fDefaultConsistencyChecks = false;
|
|
249
239
|
fRequireStandard = false;
|
|
250
240
|
m_is_test_chain = true;
|
|
@@ -252,7 +242,18 @@ public:
|
|
|
252
242
|
|
|
253
243
|
checkpointData = {
|
|
254
244
|
{
|
|
255
|
-
{
|
|
245
|
+
{ 0, uint256S("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06")},
|
|
246
|
+
{ 19080, uint256S("0xb054d63d41852d71b611eaa8eca37d9fddca69b5013cf0966d453402ec8005ce")},
|
|
247
|
+
{ 30583, uint256S("0x5179c0c496b5d25ab81ffe14273ea6928c6ff81c0a0d6a83b5d7d41d64886300")},
|
|
248
|
+
{ 99999, uint256S("0xa7b03b14b8673683d972ab81775f3e85fea4fe689874b5956183466535dc651c")},
|
|
249
|
+
{219999, uint256S("0x0691bb86c92762c5c4c5a3723585ebeb7ec59310bbb0bdb6666551ab24ad919e")},
|
|
250
|
+
{336000, uint256S("0xf07adf61615c529f7c282b858d13d3e037b197324cb12e0669c461947494c4e3")},
|
|
251
|
+
{372751, uint256S("0x000000000000148db599b217c117b5104f5043c55f6ca2a8a065d9fab9f9bba1")},
|
|
252
|
+
{382019, uint256S("0x3ab75769d7957d9bf0857b5019d0a0e41044fa9ecf30b2f9c32aa457b0864ce5")},
|
|
253
|
+
{408500, uint256S("0x1636ac08b073d26b28fa40243d58dd5deb215752efe094c92c61998e4e9baf3f")},
|
|
254
|
+
{412691, uint256S("0x0e20318be88f07f521453435b37cfc516c3de07264a78ed7170985a1126126ab")},
|
|
255
|
+
{441299, uint256S("0x4091d0836a37c50ceee876000ac0cb251fd10031dab901d2c0677cd86283096e")},
|
|
256
|
+
{442735, uint256S("0x1b83b33894d51be0b8b323bfab093f638915236e0e40ba3b52bb33fdbc4053cd")},
|
|
256
257
|
}
|
|
257
258
|
};
|
|
258
259
|
|
|
@@ -261,10 +262,14 @@ public:
|
|
|
261
262
|
};
|
|
262
263
|
|
|
263
264
|
chainTxData = ChainTxData{
|
|
264
|
-
// Data
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
// Data as of block 0x1b83b33894d51be0b8b323bfab093f638915236e0e40ba3b52bb33fdbc4053cd (height 442735)
|
|
266
|
+
1632053274, // * UNIX timestamp of last known number of transactions
|
|
267
|
+
863997, // * total number of transactions between genesis and that timestamp
|
|
268
|
+
|
|
269
|
+
// (the tx=... number in the SetBestChain debug.log lines)
|
|
270
|
+
0.003020718 // * estimated number of transactions per second after that timestamp
|
|
271
|
+
// 863997/(1632053274-1346029522) = 0.003020718
|
|
272
|
+
|
|
268
273
|
};
|
|
269
274
|
}
|
|
270
275
|
};
|
|
@@ -289,7 +294,6 @@ public:
|
|
|
289
294
|
consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000de26b0e471");
|
|
290
295
|
consensus.defaultAssumeValid = uint256S("0x00000112852484b5fe3451572368f93cfd2723279af3464e478aee35115256ef"); // 78788
|
|
291
296
|
m_assumed_blockchain_size = 1;
|
|
292
|
-
m_assumed_chain_state_size = 0;
|
|
293
297
|
chainTxData = ChainTxData{
|
|
294
298
|
// Data from RPC: getchaintxstats 4096 0000003d9144c56ac110ae04a0c271a0acce2f14f426b39fdf0d938c96d2eb09
|
|
295
299
|
/* nTime */ 1645631279,
|
|
@@ -306,7 +310,6 @@ public:
|
|
|
306
310
|
consensus.nMinimumChainWork = uint256{};
|
|
307
311
|
consensus.defaultAssumeValid = uint256{};
|
|
308
312
|
m_assumed_blockchain_size = 0;
|
|
309
|
-
m_assumed_chain_state_size = 0;
|
|
310
313
|
chainTxData = ChainTxData{
|
|
311
314
|
0,
|
|
312
315
|
0,
|
|
@@ -322,15 +325,12 @@ public:
|
|
|
322
325
|
strNetworkID = CBaseChainParams::SIGNET;
|
|
323
326
|
consensus.signet_blocks = true;
|
|
324
327
|
consensus.signet_challenge.assign(bin.begin(), bin.end());
|
|
325
|
-
consensus.nSubsidyHalvingInterval = 210000;
|
|
326
328
|
consensus.BIP16Exception = uint256{};
|
|
327
329
|
consensus.BIP34Height = 1;
|
|
328
330
|
consensus.BIP34Hash = uint256{};
|
|
329
|
-
consensus.BIP65Height = 1;
|
|
330
|
-
consensus.BIP66Height = 1;
|
|
331
331
|
consensus.CSVHeight = 1;
|
|
332
332
|
consensus.SegwitHeight = 1;
|
|
333
|
-
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
|
|
333
|
+
// consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
|
|
334
334
|
consensus.nPowTargetSpacing = 10 * 60;
|
|
335
335
|
consensus.fPowAllowMinDifficultyBlocks = false;
|
|
336
336
|
consensus.fPowNoRetargeting = false;
|
|
@@ -338,6 +338,7 @@ public:
|
|
|
338
338
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
|
339
339
|
consensus.MinBIP9WarningHeight = 0;
|
|
340
340
|
consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000");
|
|
341
|
+
/*
|
|
341
342
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
|
342
343
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
|
343
344
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
@@ -348,7 +349,7 @@ public:
|
|
|
348
349
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
|
349
350
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
350
351
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
|
|
351
|
-
|
|
352
|
+
*/
|
|
352
353
|
// message start is defined as the first 4 bytes of the sha256d of the block script
|
|
353
354
|
CHashWriter h(SER_DISK, 0);
|
|
354
355
|
h << consensus.signet_challenge;
|
|
@@ -358,10 +359,10 @@ public:
|
|
|
358
359
|
nDefaultPort = 38333;
|
|
359
360
|
nPruneAfterHeight = 1000;
|
|
360
361
|
|
|
361
|
-
genesis = CreateGenesisBlock(
|
|
362
|
+
genesis = CreateGenesisBlock(1345083810, 1345090000, 122894938, 0x1d0fffff, 1, 0);
|
|
362
363
|
consensus.hashGenesisBlock = genesis.GetHash();
|
|
363
|
-
assert(consensus.hashGenesisBlock == uint256S("
|
|
364
|
-
assert(genesis.hashMerkleRoot == uint256S("
|
|
364
|
+
assert(consensus.hashGenesisBlock == uint256S("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06"));
|
|
365
|
+
assert(genesis.hashMerkleRoot == uint256S("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2"));
|
|
365
366
|
|
|
366
367
|
vFixedSeeds.clear();
|
|
367
368
|
|
|
@@ -384,70 +385,67 @@ public:
|
|
|
384
385
|
* Regression test: intended for private networks only. Has minimal difficulty to ensure that
|
|
385
386
|
* blocks can be found instantly.
|
|
386
387
|
*/
|
|
388
|
+
|
|
387
389
|
class CRegTestParams : public CChainParams {
|
|
388
390
|
public:
|
|
389
391
|
explicit CRegTestParams(const ArgsManager& args) {
|
|
390
392
|
strNetworkID = CBaseChainParams::REGTEST;
|
|
391
393
|
consensus.signet_blocks = false;
|
|
392
394
|
consensus.signet_challenge.clear();
|
|
393
|
-
consensus.nSubsidyHalvingInterval = 150;
|
|
394
395
|
consensus.BIP16Exception = uint256();
|
|
395
396
|
consensus.BIP34Height = 1; // Always active unless overridden
|
|
396
397
|
consensus.BIP34Hash = uint256();
|
|
397
|
-
consensus.
|
|
398
|
-
consensus.
|
|
399
|
-
|
|
400
|
-
consensus.
|
|
401
|
-
consensus.
|
|
402
|
-
consensus.
|
|
403
|
-
consensus.
|
|
404
|
-
|
|
398
|
+
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 28;
|
|
399
|
+
consensus.bnInitialHashTarget = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 29;
|
|
400
|
+
|
|
401
|
+
consensus.nTargetTimespan = 7 * 24 * 60 * 60; // two weeks
|
|
402
|
+
consensus.nStakeTargetSpacing = 10 * 60; // 10-minute block spacing
|
|
403
|
+
consensus.nTargetSpacingWorkMax = 12 * consensus.nStakeTargetSpacing; // 2-hour
|
|
404
|
+
consensus.nPowTargetSpacing = consensus.nStakeTargetSpacing;
|
|
405
|
+
|
|
406
|
+
consensus.nStakeMinAge = 60 * 60 * 24; // test net min age is 1 day
|
|
407
|
+
consensus.nStakeMaxAge = 60 * 60 * 24 * 90;
|
|
408
|
+
consensus.nModifierInterval = 60 * 20; // Modifier interval: time to elapse before new modifier is computed
|
|
409
|
+
consensus.nCoinbaseMaturity = 60;
|
|
410
|
+
|
|
405
411
|
consensus.fPowAllowMinDifficultyBlocks = true;
|
|
406
412
|
consensus.fPowNoRetargeting = true;
|
|
407
413
|
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains
|
|
408
414
|
consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
|
|
409
|
-
|
|
410
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
|
411
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
|
|
412
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
415
|
+
/*
|
|
413
416
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
|
|
414
417
|
|
|
415
418
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
|
416
419
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
|
417
420
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
418
421
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
|
|
419
|
-
|
|
422
|
+
*/
|
|
420
423
|
consensus.nMinimumChainWork = uint256{};
|
|
421
424
|
consensus.defaultAssumeValid = uint256{};
|
|
422
425
|
|
|
423
|
-
pchMessageStart[0] =
|
|
424
|
-
pchMessageStart[1] =
|
|
425
|
-
pchMessageStart[2] =
|
|
426
|
-
pchMessageStart[3] =
|
|
427
|
-
nDefaultPort =
|
|
428
|
-
nPruneAfterHeight =
|
|
426
|
+
pchMessageStart[0] = 0xcb;
|
|
427
|
+
pchMessageStart[1] = 0xf2;
|
|
428
|
+
pchMessageStart[2] = 0xc0;
|
|
429
|
+
pchMessageStart[3] = 0xef;
|
|
430
|
+
nDefaultPort = 9903;
|
|
431
|
+
nPruneAfterHeight = 1000;
|
|
429
432
|
m_assumed_blockchain_size = 0;
|
|
430
|
-
m_assumed_chain_state_size = 0;
|
|
431
433
|
|
|
432
|
-
|
|
434
|
+
genesis = CreateGenesisBlock(1345083810, 1345090000, 122894938, 0x1d0fffff, 1, 0);
|
|
433
435
|
|
|
434
|
-
genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN);
|
|
435
436
|
consensus.hashGenesisBlock = genesis.GetHash();
|
|
436
|
-
assert(consensus.hashGenesisBlock == uint256S("
|
|
437
|
-
assert(genesis.hashMerkleRoot == uint256S("
|
|
437
|
+
assert(consensus.hashGenesisBlock == uint256S("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06"));
|
|
438
|
+
assert(genesis.hashMerkleRoot == uint256S("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2"));
|
|
438
439
|
|
|
439
440
|
vFixedSeeds.clear(); //!< Regtest mode doesn't have any fixed seeds.
|
|
440
441
|
vSeeds.clear();
|
|
441
442
|
vSeeds.emplace_back("dummySeed.invalid.");
|
|
442
443
|
|
|
443
|
-
fDefaultConsistencyChecks = true;
|
|
444
|
-
fRequireStandard = true;
|
|
445
444
|
m_is_test_chain = true;
|
|
446
445
|
m_is_mockable_chain = true;
|
|
447
|
-
|
|
448
446
|
checkpointData = {
|
|
449
447
|
{
|
|
450
|
-
{0, uint256S("
|
|
448
|
+
{0, uint256S("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06")},
|
|
451
449
|
}
|
|
452
450
|
};
|
|
453
451
|
|
|
@@ -474,17 +472,11 @@ public:
|
|
|
474
472
|
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
|
475
473
|
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
|
476
474
|
|
|
477
|
-
bech32_hrp = "
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
*/
|
|
483
|
-
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int min_activation_height)
|
|
484
|
-
{
|
|
485
|
-
consensus.vDeployments[d].nStartTime = nStartTime;
|
|
486
|
-
consensus.vDeployments[d].nTimeout = nTimeout;
|
|
487
|
-
consensus.vDeployments[d].min_activation_height = min_activation_height;
|
|
475
|
+
bech32_hrp = "pcrt";
|
|
476
|
+
fMiningRequiresPeers = false;
|
|
477
|
+
fDefaultConsistencyChecks = true;
|
|
478
|
+
fRequireStandard = false;
|
|
479
|
+
//fMineBlocksOnDemand = true;
|
|
488
480
|
}
|
|
489
481
|
void UpdateActivationParametersFromArgs(const ArgsManager& args);
|
|
490
482
|
};
|
|
@@ -507,9 +499,9 @@ static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& conse
|
|
|
507
499
|
} else if (name == "bip34") {
|
|
508
500
|
consensus.BIP34Height = int{height};
|
|
509
501
|
} else if (name == "dersig") {
|
|
510
|
-
consensus.BIP66Height = int{height};
|
|
502
|
+
//consensus.BIP66Height = int{height};
|
|
511
503
|
} else if (name == "cltv") {
|
|
512
|
-
consensus.BIP65Height = int{height};
|
|
504
|
+
//consensus.BIP65Height = int{height};
|
|
513
505
|
} else if (name == "csv") {
|
|
514
506
|
consensus.CSVHeight = int{height};
|
|
515
507
|
} else {
|
|
@@ -521,40 +513,7 @@ static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& conse
|
|
|
521
513
|
void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
|
|
522
514
|
{
|
|
523
515
|
MaybeUpdateHeights(args, consensus);
|
|
524
|
-
|
|
525
|
-
if (!args.IsArgSet("-vbparams")) return;
|
|
526
|
-
|
|
527
|
-
for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
|
|
528
|
-
std::vector<std::string> vDeploymentParams;
|
|
529
|
-
boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
|
|
530
|
-
if (vDeploymentParams.size() < 3 || 4 < vDeploymentParams.size()) {
|
|
531
|
-
throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height]");
|
|
532
|
-
}
|
|
533
|
-
int64_t nStartTime, nTimeout;
|
|
534
|
-
int min_activation_height = 0;
|
|
535
|
-
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
|
|
536
|
-
throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
|
|
537
|
-
}
|
|
538
|
-
if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
|
|
539
|
-
throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
|
|
540
|
-
}
|
|
541
|
-
if (vDeploymentParams.size() >= 4 && !ParseInt32(vDeploymentParams[3], &min_activation_height)) {
|
|
542
|
-
throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3]));
|
|
543
|
-
}
|
|
544
|
-
bool found = false;
|
|
545
|
-
for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
|
|
546
|
-
if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) {
|
|
547
|
-
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, min_activation_height);
|
|
548
|
-
found = true;
|
|
549
|
-
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d\n", vDeploymentParams[0], nStartTime, nTimeout, min_activation_height);
|
|
550
|
-
break;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
if (!found) {
|
|
554
|
-
throw std::runtime_error(strprintf("Invalid deployment (%s)", vDeploymentParams[0]));
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
516
|
+
}
|
|
558
517
|
|
|
559
518
|
static std::unique_ptr<const CChainParams> globalChainParams;
|
|
560
519
|
|
|
@@ -93,6 +93,8 @@ public:
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
const CBlock& GenesisBlock() const { return genesis; }
|
|
96
|
+
/** Make miner wait to have peers to avoid wasting work */
|
|
97
|
+
bool MiningRequiresPeers() const { return fMiningRequiresPeers; }
|
|
96
98
|
/** Default value for -checkmempool and -checkblockindex argument */
|
|
97
99
|
bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; }
|
|
98
100
|
/** Policy: Filter transactions that do not match well-defined patterns */
|
|
@@ -104,8 +106,6 @@ public:
|
|
|
104
106
|
uint64_t PruneAfterHeight() const { return nPruneAfterHeight; }
|
|
105
107
|
/** Minimum free space (in GB) needed for data directory */
|
|
106
108
|
uint64_t AssumedBlockchainSize() const { return m_assumed_blockchain_size; }
|
|
107
|
-
/** Minimum free space (in GB) needed for data directory when pruned; Does not include prune target*/
|
|
108
|
-
uint64_t AssumedChainStateSize() const { return m_assumed_chain_state_size; }
|
|
109
109
|
/** Whether it is possible to mine blocks on demand (no retargeting) */
|
|
110
110
|
bool MineBlocksOnDemand() const { return consensus.fPowNoRetargeting; }
|
|
111
111
|
/** Return the network string */
|
|
@@ -130,13 +130,13 @@ protected:
|
|
|
130
130
|
uint16_t nDefaultPort;
|
|
131
131
|
uint64_t nPruneAfterHeight;
|
|
132
132
|
uint64_t m_assumed_blockchain_size;
|
|
133
|
-
uint64_t m_assumed_chain_state_size;
|
|
134
133
|
std::vector<std::string> vSeeds;
|
|
135
134
|
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
|
|
136
135
|
std::string bech32_hrp;
|
|
137
136
|
std::string strNetworkID;
|
|
138
137
|
CBlock genesis;
|
|
139
138
|
std::vector<uint8_t> vFixedSeeds;
|
|
139
|
+
bool fMiningRequiresPeers;
|
|
140
140
|
bool fDefaultConsistencyChecks;
|
|
141
141
|
bool fRequireStandard;
|
|
142
142
|
bool m_is_test_chain;
|
|
@@ -43,13 +43,13 @@ const CBaseChainParams& BaseParams()
|
|
|
43
43
|
std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain)
|
|
44
44
|
{
|
|
45
45
|
if (chain == CBaseChainParams::MAIN) {
|
|
46
|
-
return std::make_unique<CBaseChainParams>("",
|
|
46
|
+
return std::make_unique<CBaseChainParams>("", 9902, 9903);
|
|
47
47
|
} else if (chain == CBaseChainParams::TESTNET) {
|
|
48
|
-
return std::make_unique<CBaseChainParams>("testnet3",
|
|
48
|
+
return std::make_unique<CBaseChainParams>("testnet3", 9904, 9905);
|
|
49
49
|
} else if (chain == CBaseChainParams::SIGNET) {
|
|
50
50
|
return std::make_unique<CBaseChainParams>("signet", 38332, 38334);
|
|
51
51
|
} else if (chain == CBaseChainParams::REGTEST) {
|
|
52
|
-
return std::make_unique<CBaseChainParams>("regtest", 18443,
|
|
52
|
+
return std::make_unique<CBaseChainParams>("regtest", 18443, 18444);
|
|
53
53
|
}
|
|
54
54
|
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
|
|
55
55
|
}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
const std::string CLIENT_NAME("Satoshi");
|
|
20
20
|
|
|
21
|
+
#define CLIENT_VERSION_SUFFIX " Coccinellidae"
|
|
21
22
|
|
|
22
23
|
#ifdef HAVE_BUILD_INFO
|
|
23
24
|
#include <obj/build.h>
|
|
@@ -34,7 +35,8 @@ const std::string CLIENT_NAME("Satoshi");
|
|
|
34
35
|
#define BUILD_DESC BUILD_GIT_TAG
|
|
35
36
|
#define BUILD_SUFFIX ""
|
|
36
37
|
#else
|
|
37
|
-
#define BUILD_DESC "v"
|
|
38
|
+
#define BUILD_DESC "v" STRINGIZE(PEERCOIN_VERSION_MAJOR) "." STRINGIZE(PEERCOIN_VERSION_MINOR) \
|
|
39
|
+
"." STRINGIZE(PEERCOIN_VERSION_REVISION) "." STRINGIZE(PEERCOIN_VERSION_BUILD)
|
|
38
40
|
#if CLIENT_VERSION_IS_RELEASE
|
|
39
41
|
#define BUILD_SUFFIX ""
|
|
40
42
|
#elif defined(BUILD_GIT_COMMIT)
|
|
@@ -53,7 +55,7 @@ static std::string FormatVersion(int nVersion)
|
|
|
53
55
|
|
|
54
56
|
std::string FormatFullVersion()
|
|
55
57
|
{
|
|
56
|
-
static const std::string CLIENT_BUILD(BUILD_DESC
|
|
58
|
+
static const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX);
|
|
57
59
|
return CLIENT_BUILD;
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -74,6 +76,8 @@ std::string FormatSubVersion(const std::string& name, int nClientVersion, const
|
|
|
74
76
|
ss << ")";
|
|
75
77
|
}
|
|
76
78
|
ss << "/";
|
|
79
|
+
ss << "Peercoin:" << FormatVersion(PEERCOIN_VERSION);
|
|
80
|
+
ss << "(" << FormatFullVersion() << ")/";
|
|
77
81
|
return ss.str();
|
|
78
82
|
}
|
|
79
83
|
|
|
@@ -91,7 +95,7 @@ std::string CopyrightHolders(const std::string& strPrefix)
|
|
|
91
95
|
|
|
92
96
|
std::string LicenseInfo()
|
|
93
97
|
{
|
|
94
|
-
const std::string URL_SOURCE_CODE = "<https://github.com/
|
|
98
|
+
const std::string URL_SOURCE_CODE = "<https://github.com/peercoin/peercoin>";
|
|
95
99
|
|
|
96
100
|
return CopyrightHolders(strprintf(_("Copyright (C) %i-%i").translated, 2009, COPYRIGHT_YEAR) + " ") + "\n" +
|
|
97
101
|
"\n" +
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
#endif //HAVE_CONFIG_H
|
|
13
13
|
|
|
14
14
|
// Check that required client information is defined
|
|
15
|
-
#if !defined(
|
|
15
|
+
#if !defined(PEERCOIN_VERSION_MAJOR) || !defined(PEERCOIN_VERSION_MINOR) || !defined(PEERCOIN_VERSION_REVISION) || !defined(PEERCOIN_VERSION_BUILD)
|
|
16
16
|
#error Client version information missing: version is not defined by bitcoin-config.h or in any other way
|
|
17
17
|
#endif
|
|
18
18
|
|
|
@@ -35,6 +35,13 @@ static const int CLIENT_VERSION =
|
|
|
35
35
|
+ 100 * CLIENT_VERSION_MINOR
|
|
36
36
|
+ 1 * CLIENT_VERSION_BUILD;
|
|
37
37
|
|
|
38
|
+
// note: peercoin version is used for display purpose AND to accept alerts
|
|
39
|
+
static const int PEERCOIN_VERSION =
|
|
40
|
+
1000000 * PEERCOIN_VERSION_MAJOR
|
|
41
|
+
+ 10000 * PEERCOIN_VERSION_MINOR
|
|
42
|
+
+ 100 * PEERCOIN_VERSION_REVISION
|
|
43
|
+
+ 1 * PEERCOIN_VERSION_BUILD;
|
|
44
|
+
|
|
38
45
|
extern const std::string CLIENT_NAME;
|
|
39
46
|
|
|
40
47
|
|
|
@@ -64,9 +64,10 @@ bool CCoinsViewCache::GetCoin(const COutPoint &outpoint, Coin &coin) const {
|
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {
|
|
67
|
+
void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite, bool skipZeroValue) {
|
|
68
68
|
assert(!coin.IsSpent());
|
|
69
69
|
if (coin.out.scriptPubKey.IsUnspendable()) return;
|
|
70
|
+
if (coin.out.nValue == 0 && skipZeroValue) return;
|
|
70
71
|
CCoinsMap::iterator it;
|
|
71
72
|
bool inserted;
|
|
72
73
|
std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::tuple<>());
|
|
@@ -112,14 +113,14 @@ void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coi
|
|
|
112
113
|
std::forward_as_tuple(std::move(coin), CCoinsCacheEntry::DIRTY));
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {
|
|
116
|
+
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite, bool skipZeroValue) {
|
|
116
117
|
bool fCoinbase = tx.IsCoinBase();
|
|
117
118
|
const uint256& txid = tx.GetHash();
|
|
118
119
|
for (size_t i = 0; i < tx.vout.size(); ++i) {
|
|
119
120
|
bool overwrite = check_for_overwrite ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase;
|
|
120
121
|
// Coinbase transactions can always be overwritten, in order to correctly
|
|
121
122
|
// deal with the pre-BIP30 occurrences of duplicate coinbase transactions.
|
|
122
|
-
cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite);
|
|
123
|
+
cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, tx.IsCoinStake(), tx.nTime), overwrite, skipZeroValue);
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -39,29 +39,48 @@ public:
|
|
|
39
39
|
//! at which height this containing transaction was included in the active block chain
|
|
40
40
|
uint32_t nHeight : 31;
|
|
41
41
|
|
|
42
|
+
// peercoin: whether transaction is a coinstake
|
|
43
|
+
bool fCoinStake;
|
|
44
|
+
|
|
45
|
+
// peercoin: transaction timestamp
|
|
46
|
+
unsigned int nTime;
|
|
47
|
+
|
|
42
48
|
//! construct a Coin from a CTxOut and height/coinbase information.
|
|
43
|
-
Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn
|
|
44
|
-
|
|
49
|
+
Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn, bool fCoinStakeIn, int nTimeIn) :
|
|
50
|
+
out(std::move(outIn)), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), fCoinStake(fCoinStakeIn), nTime(nTimeIn) {}
|
|
51
|
+
Coin(const CTxOut& outIn, int nHeightIn, bool fCoinBaseIn, bool fCoinStakeIn, int nTimeIn) :
|
|
52
|
+
out(outIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), fCoinStake(fCoinStakeIn), nTime(nTimeIn) {}
|
|
45
53
|
|
|
46
54
|
void Clear() {
|
|
47
55
|
out.SetNull();
|
|
48
56
|
fCoinBase = false;
|
|
49
57
|
nHeight = 0;
|
|
58
|
+
fCoinStake = false;
|
|
59
|
+
nTime = 0;
|
|
50
60
|
}
|
|
51
61
|
|
|
52
62
|
//! empty constructor
|
|
53
|
-
Coin() : fCoinBase(false), nHeight(0) { }
|
|
63
|
+
Coin() : fCoinBase(false), nHeight(0), fCoinStake(false), nTime(0) { }
|
|
54
64
|
|
|
55
65
|
bool IsCoinBase() const {
|
|
56
66
|
return fCoinBase;
|
|
57
67
|
}
|
|
58
68
|
|
|
69
|
+
bool IsCoinStake() const { // peercoin: coinstake
|
|
70
|
+
return fCoinStake;
|
|
71
|
+
}
|
|
72
|
+
|
|
59
73
|
template<typename Stream>
|
|
60
74
|
void Serialize(Stream &s) const {
|
|
61
75
|
assert(!IsSpent());
|
|
62
76
|
uint32_t code = nHeight * uint32_t{2} + fCoinBase;
|
|
63
77
|
::Serialize(s, VARINT(code));
|
|
64
78
|
::Serialize(s, Using<TxOutCompression>(out));
|
|
79
|
+
// peercoin flags
|
|
80
|
+
unsigned int nFlag = fCoinStake? 1 : 0;
|
|
81
|
+
::Serialize(s, VARINT(nFlag));
|
|
82
|
+
// peercoin transaction timestamp
|
|
83
|
+
::Serialize(s, VARINT(nTime));
|
|
65
84
|
}
|
|
66
85
|
|
|
67
86
|
template<typename Stream>
|
|
@@ -71,6 +90,12 @@ public:
|
|
|
71
90
|
nHeight = code >> 1;
|
|
72
91
|
fCoinBase = code & 1;
|
|
73
92
|
::Unserialize(s, Using<TxOutCompression>(out));
|
|
93
|
+
// peercoin flags
|
|
94
|
+
unsigned int nFlag = 0;
|
|
95
|
+
::Unserialize(s, VARINT(nFlag));
|
|
96
|
+
fCoinStake = nFlag & 1;
|
|
97
|
+
// peercoin transaction timestamp
|
|
98
|
+
::Unserialize(s, VARINT(nTime));
|
|
74
99
|
}
|
|
75
100
|
|
|
76
101
|
/** Either this coin never existed (see e.g. coinEmpty in coins.cpp), or it
|
|
@@ -264,7 +289,7 @@ public:
|
|
|
264
289
|
* Add a coin. Set possible_overwrite to true if an unspent version may
|
|
265
290
|
* already exist in the cache.
|
|
266
291
|
*/
|
|
267
|
-
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite);
|
|
292
|
+
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite, bool skipZeroValue = false);
|
|
268
293
|
|
|
269
294
|
/**
|
|
270
295
|
* Emplace a coin into cacheCoins without performing any checks, marking
|
|
@@ -325,7 +350,7 @@ private:
|
|
|
325
350
|
//! an overwrite.
|
|
326
351
|
// TODO: pass in a boolean to limit these possible overwrites to known
|
|
327
352
|
// (pre-BIP34) cases.
|
|
328
|
-
void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, bool check = false);
|
|
353
|
+
void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, bool check = false, bool skipZeroValue = false);
|
|
329
354
|
|
|
330
355
|
//! Utility function to find any unspent output with a given txid.
|
|
331
356
|
//! This function can be quite expensive because in the event of a transaction
|
|
@@ -7,12 +7,22 @@
|
|
|
7
7
|
#define BITCOIN_CONSENSUS_AMOUNT_H
|
|
8
8
|
|
|
9
9
|
#include <cstdint>
|
|
10
|
+
#include <string>
|
|
10
11
|
|
|
11
12
|
/** Amount in satoshis (Can be negative) */
|
|
12
13
|
typedef int64_t CAmount;
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
static constexpr CAmount
|
|
15
|
+
static constexpr CAmount COIN = 1000000;
|
|
16
|
+
static constexpr CAmount CENT = 10000;
|
|
17
|
+
|
|
18
|
+
static const CAmount MIN_TX_FEE_PREV7 = CENT;
|
|
19
|
+
static const CAmount MIN_TX_FEE = CENT / 10;
|
|
20
|
+
static const CAmount PERKB_TX_FEE = CENT;
|
|
21
|
+
static const CAmount MIN_TXOUT_AMOUNT = CENT;
|
|
22
|
+
static const CAmount MAX_MINT_PROOF_OF_WORK = 9999 * COIN;
|
|
23
|
+
static const CAmount MAX_MINT_PROOF_OF_WORK_V10 = 50 * COIN;
|
|
24
|
+
static const std::string CURRENCY_UNIT = "PPC";
|
|
25
|
+
static const std::string CURRENCY_ATOM = "sat"; // One indivisible minimum value unit
|
|
16
26
|
|
|
17
27
|
/** No amount larger than this (in satoshi) is valid.
|
|
18
28
|
*
|
|
@@ -33,52 +33,16 @@ enum DeploymentPos : uint16_t {
|
|
|
33
33
|
};
|
|
34
34
|
constexpr bool ValidDeployment(DeploymentPos dep) { return dep < MAX_VERSION_BITS_DEPLOYMENTS; }
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
* Struct for each individual consensus rule change using BIP9.
|
|
38
|
-
*/
|
|
39
|
-
struct BIP9Deployment {
|
|
40
|
-
/** Bit position to select the particular bit in nVersion. */
|
|
41
|
-
int bit;
|
|
42
|
-
/** Start MedianTime for version bits miner confirmation. Can be a date in the past */
|
|
43
|
-
int64_t nStartTime;
|
|
44
|
-
/** Timeout/expiry MedianTime for the deployment attempt. */
|
|
45
|
-
int64_t nTimeout;
|
|
46
|
-
/** If lock in occurs, delay activation until at least this block
|
|
47
|
-
* height. Note that activation will only occur on a retarget
|
|
48
|
-
* boundary.
|
|
49
|
-
*/
|
|
50
|
-
int min_activation_height{0};
|
|
51
|
-
|
|
52
|
-
/** Constant for nTimeout very far in the future. */
|
|
53
|
-
static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();
|
|
54
|
-
|
|
55
|
-
/** Special value for nStartTime indicating that the deployment is always active.
|
|
56
|
-
* This is useful for testing, as it means tests don't need to deal with the activation
|
|
57
|
-
* process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
|
|
58
|
-
* behaviour during activation cannot use this. */
|
|
59
|
-
static constexpr int64_t ALWAYS_ACTIVE = -1;
|
|
60
|
-
|
|
61
|
-
/** Special value for nStartTime indicating that the deployment is never active.
|
|
62
|
-
* This is useful for integrating the code changes for a new feature
|
|
63
|
-
* prior to deploying it on some or all networks. */
|
|
64
|
-
static constexpr int64_t NEVER_ACTIVE = -2;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
36
|
/**
|
|
68
37
|
* Parameters that influence chain consensus.
|
|
69
38
|
*/
|
|
70
39
|
struct Params {
|
|
71
40
|
uint256 hashGenesisBlock;
|
|
72
|
-
int nSubsidyHalvingInterval;
|
|
73
41
|
/* Block hash that is excepted from BIP16 enforcement */
|
|
74
42
|
uint256 BIP16Exception;
|
|
75
43
|
/** Block height and hash at which BIP34 becomes active */
|
|
76
44
|
int BIP34Height;
|
|
77
45
|
uint256 BIP34Hash;
|
|
78
|
-
/** Block height at which BIP65 becomes active */
|
|
79
|
-
int BIP65Height;
|
|
80
|
-
/** Block height at which BIP66 becomes active */
|
|
81
|
-
int BIP66Height;
|
|
82
46
|
/** Block height at which CSV (BIP68, BIP112 and BIP113) becomes active */
|
|
83
47
|
int CSVHeight;
|
|
84
48
|
/** Block height at which Segwit (BIP141, BIP143 and BIP147) becomes active.
|
|
@@ -90,19 +54,16 @@ struct Params {
|
|
|
90
54
|
int MinBIP9WarningHeight;
|
|
91
55
|
/**
|
|
92
56
|
* Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period,
|
|
93
|
-
* (nPowTargetTimespan / nPowTargetSpacing)
|
|
57
|
+
* (nPowTargetTimespan / nPowTargetSpacing)
|
|
94
58
|
* Examples: 1916 for 95%, 1512 for testchains.
|
|
95
59
|
*/
|
|
96
60
|
uint32_t nRuleChangeActivationThreshold;
|
|
97
61
|
uint32_t nMinerConfirmationWindow;
|
|
98
|
-
BIP9Deployment vDeployments[MAX_VERSION_BITS_DEPLOYMENTS];
|
|
99
62
|
/** Proof of work parameters */
|
|
100
63
|
uint256 powLimit;
|
|
101
64
|
bool fPowAllowMinDifficultyBlocks;
|
|
102
65
|
bool fPowNoRetargeting;
|
|
103
66
|
int64_t nPowTargetSpacing;
|
|
104
|
-
int64_t nPowTargetTimespan;
|
|
105
|
-
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
|
|
106
67
|
/** The best chain should have at least this much work */
|
|
107
68
|
uint256 nMinimumChainWork;
|
|
108
69
|
/** By default assume that the signatures in ancestors of this block are valid */
|
|
@@ -114,23 +75,15 @@ struct Params {
|
|
|
114
75
|
*/
|
|
115
76
|
bool signet_blocks{false};
|
|
116
77
|
std::vector<uint8_t> signet_challenge;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return BIP66Height;
|
|
127
|
-
case DEPLOYMENT_CSV:
|
|
128
|
-
return CSVHeight;
|
|
129
|
-
case DEPLOYMENT_SEGWIT:
|
|
130
|
-
return SegwitHeight;
|
|
131
|
-
} // no default case, so the compiler can warn about missing cases
|
|
132
|
-
return std::numeric_limits<int>::max();
|
|
133
|
-
}
|
|
78
|
+
/** peercoin stuff */
|
|
79
|
+
uint256 bnInitialHashTarget;
|
|
80
|
+
int64_t nStakeTargetSpacing;
|
|
81
|
+
int64_t nTargetSpacingWorkMax;
|
|
82
|
+
int64_t nTargetTimespan;
|
|
83
|
+
int64_t nStakeMinAge;
|
|
84
|
+
int64_t nStakeMaxAge;
|
|
85
|
+
int64_t nModifierInterval;
|
|
86
|
+
int nCoinbaseMaturity; // Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
|
|
134
87
|
};
|
|
135
88
|
|
|
136
89
|
} // namespace Consensus
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
#include <consensus/amount.h>
|
|
8
8
|
#include <primitives/transaction.h>
|
|
9
9
|
#include <consensus/validation.h>
|
|
10
|
+
#include <chainparams.h>
|
|
11
|
+
|
|
12
|
+
bool IsZeroAllowed(const unsigned int nTimeTx)
|
|
13
|
+
{
|
|
14
|
+
return (nTimeTx >= 1447700000 ); // very crude approximation to prevent linking with kernel.cpp
|
|
15
|
+
}
|
|
10
16
|
|
|
11
17
|
bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
|
|
12
18
|
{
|
|
@@ -30,6 +36,10 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
|
|
|
30
36
|
nValueOut += txout.nValue;
|
|
31
37
|
if (!MoneyRange(nValueOut))
|
|
32
38
|
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txouttotal-toolarge");
|
|
39
|
+
// peercoin: enforce minimum output amount
|
|
40
|
+
if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT &&
|
|
41
|
+
(tx.nVersion < 3 && !(IsZeroAllowed(tx.nTime) && (txout.nValue == 0))))
|
|
42
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txoutvalue-belowminimum");
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
// Check for duplicate inputs (see CVE-2018-17144)
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
#include <consensus/validation.h>
|
|
12
12
|
#include <primitives/transaction.h>
|
|
13
13
|
#include <script/interpreter.h>
|
|
14
|
+
#include <kernel.h>
|
|
15
|
+
#include <validation.h> // GetCoinAge()
|
|
16
|
+
|
|
14
17
|
#include <util/moneystr.h>
|
|
15
18
|
|
|
16
19
|
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
|
|
@@ -164,7 +167,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
|
|
|
164
167
|
return nSigOps;
|
|
165
168
|
}
|
|
166
169
|
|
|
167
|
-
bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
|
|
170
|
+
bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, const Consensus::Params& params, unsigned int nTimeTx, uint64_t nMoneySupply)
|
|
168
171
|
{
|
|
169
172
|
// are the actual inputs available?
|
|
170
173
|
if (!inputs.HaveInputs(tx)) {
|
|
@@ -179,11 +182,15 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
|
|
|
179
182
|
assert(!coin.IsSpent());
|
|
180
183
|
|
|
181
184
|
// If prev is coinbase, check that it's matured
|
|
182
|
-
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight <
|
|
183
|
-
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase",
|
|
185
|
+
if ((coin.IsCoinBase() || coin.IsCoinStake()) && nSpendHeight - coin.nHeight < params.nCoinbaseMaturity) {
|
|
186
|
+
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase/coinstake",
|
|
184
187
|
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
|
185
188
|
}
|
|
186
189
|
|
|
190
|
+
// peercoin: check transaction timestamp
|
|
191
|
+
if (coin.nTime > nTimeTx)
|
|
192
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spent-too-early", strprintf("%s : transaction timestamp earlier than input transaction", __func__));
|
|
193
|
+
|
|
187
194
|
// Check for negative or overflow input values
|
|
188
195
|
nValueIn += coin.out.nValue;
|
|
189
196
|
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
|
|
@@ -191,18 +198,52 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
|
|
|
191
198
|
}
|
|
192
199
|
}
|
|
193
200
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
201
|
+
if (tx.IsCoinStake())
|
|
202
|
+
{
|
|
203
|
+
// peercoin: coin stake tx earns reward instead of paying fee
|
|
204
|
+
uint64_t nCoinAge;
|
|
205
|
+
if (!GetCoinAge(tx, inputs, nCoinAge, nTimeTx))
|
|
206
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "unable to get coin age for coinstake");
|
|
207
|
+
CAmount nStakeReward = tx.GetValueOut() - nValueIn;
|
|
208
|
+
CAmount nCoinstakeCost = (GetMinFee(tx, nTimeTx) < PERKB_TX_FEE) ? 0 : (GetMinFee(tx, nTimeTx) - PERKB_TX_FEE);
|
|
209
|
+
if (nMoneySupply && nStakeReward > GetProofOfStakeReward(nCoinAge, nTimeTx, nMoneySupply) - nCoinstakeCost)
|
|
210
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-coinstake-too-large");
|
|
198
211
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
212
|
+
else
|
|
213
|
+
{
|
|
214
|
+
const CAmount value_out = tx.GetValueOut();
|
|
215
|
+
if (nValueIn < value_out) {
|
|
216
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout",
|
|
217
|
+
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
|
|
218
|
+
}
|
|
219
|
+
// Tally transaction fees
|
|
220
|
+
const CAmount txfee_aux = nValueIn - value_out;
|
|
221
|
+
if (!MoneyRange(txfee_aux)) {
|
|
222
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange");
|
|
223
|
+
}
|
|
224
|
+
// peercoin: enforce transaction fees for every block
|
|
225
|
+
if (txfee_aux < GetMinFee(tx, nTimeTx))
|
|
226
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-not-enough");
|
|
227
|
+
txfee = txfee_aux;
|
|
204
228
|
}
|
|
205
|
-
|
|
206
|
-
txfee = txfee_aux;
|
|
207
229
|
return true;
|
|
208
230
|
}
|
|
231
|
+
|
|
232
|
+
CAmount GetMinFee(const CTransaction& tx, unsigned int nTimeTx)
|
|
233
|
+
{
|
|
234
|
+
size_t nBytes = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
|
235
|
+
return GetMinFee(nBytes, nTimeTx);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
CAmount GetMinFee(size_t nBytes, uint32_t nTime)
|
|
239
|
+
{
|
|
240
|
+
CAmount nMinFee;
|
|
241
|
+
if (IsProtocolV07(nTime) || !nTime) // RFC-0007
|
|
242
|
+
nMinFee = (nBytes < 100) ? MIN_TX_FEE : (CAmount)(nBytes * (PERKB_TX_FEE / 1000));
|
|
243
|
+
else
|
|
244
|
+
nMinFee = (1 + (CAmount)nBytes / 1000) * PERKB_TX_FEE;
|
|
245
|
+
|
|
246
|
+
if (!MoneyRange(nMinFee))
|
|
247
|
+
nMinFee = MAX_MONEY;
|
|
248
|
+
return nMinFee;
|
|
249
|
+
}
|
|
@@ -18,13 +18,14 @@ class TxValidationState;
|
|
|
18
18
|
/** Transaction validation functions */
|
|
19
19
|
|
|
20
20
|
namespace Consensus {
|
|
21
|
+
struct Params;
|
|
21
22
|
/**
|
|
22
23
|
* Check whether all inputs of this transaction are valid (no double spends and amounts)
|
|
23
24
|
* This does not modify the UTXO set. This does not check scripts and sigs.
|
|
24
25
|
* @param[out] txfee Set to the transaction fee if successful.
|
|
25
26
|
* Preconditions: tx.IsCoinBase() is false.
|
|
26
27
|
*/
|
|
27
|
-
|
|
28
|
+
bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, const Consensus::Params& params, unsigned int nTimeTx, uint64_t nMoneySupply=0);
|
|
28
29
|
} // namespace Consensus
|
|
29
30
|
|
|
30
31
|
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
|
|
@@ -75,4 +76,8 @@ bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair<int, int64_t> loc
|
|
|
75
76
|
*/
|
|
76
77
|
bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block);
|
|
77
78
|
|
|
79
|
+
// peercoin: minimum fee for transaction to be accepted in a blockchain.
|
|
80
|
+
CAmount GetMinFee(const CTransaction& tx, unsigned int nTimeTx);
|
|
81
|
+
CAmount GetMinFee(size_t nBytes, uint32_t nTime);
|
|
82
|
+
|
|
78
83
|
#endif // BITCOIN_CONSENSUS_TX_VERIFY_H
|
|
@@ -29,7 +29,7 @@ UniValue ValueFromAmount(const CAmount amount)
|
|
|
29
29
|
remainder = -remainder;
|
|
30
30
|
}
|
|
31
31
|
return UniValue(UniValue::VNUM,
|
|
32
|
-
strprintf("%s%d.%
|
|
32
|
+
strprintf("%s%d.%06d", amount < 0 ? "-" : "", quotient, remainder));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
std::string FormatScript(const CScript& script)
|
|
@@ -174,6 +174,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
|
|
|
174
174
|
// Transaction version is actually unsigned in consensus checks, just signed in memory,
|
|
175
175
|
// so cast to unsigned before giving it to the user.
|
|
176
176
|
entry.pushKV("version", static_cast<int64_t>(static_cast<uint32_t>(tx.nVersion)));
|
|
177
|
+
entry.pushKV("time", (int64_t)tx.nTime);
|
|
177
178
|
entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION));
|
|
178
179
|
entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
|
|
179
180
|
entry.pushKV("weight", GetTransactionWeight(tx));
|
|
@@ -252,8 +253,10 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
|
|
|
252
253
|
|
|
253
254
|
if (have_undo) {
|
|
254
255
|
const CAmount fee = amt_total_in - amt_total_out;
|
|
255
|
-
|
|
256
|
-
|
|
256
|
+
if (fee > 0)
|
|
257
|
+
entry.pushKV("fee", ValueFromAmount(fee));
|
|
258
|
+
else
|
|
259
|
+
entry.pushKV("reward", ValueFromAmount(-fee));
|
|
257
260
|
}
|
|
258
261
|
|
|
259
262
|
if (!hashBlock.IsNull())
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2020-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#include <deploymentstatus.h>
|
|
6
|
-
|
|
7
|
-
#include <consensus/params.h>
|
|
8
|
-
#include <versionbits.h>
|
|
9
|
-
|
|
10
|
-
#include <type_traits>
|
|
11
|
-
|
|
12
|
-
VersionBitsCache g_versionbitscache;
|
|
13
|
-
|
|
14
|
-
/* Basic sanity checking for BuriedDeployment/DeploymentPos enums and
|
|
15
|
-
* ValidDeployment check */
|
|
16
|
-
|
|
17
|
-
static_assert(ValidDeployment(Consensus::DEPLOYMENT_TESTDUMMY), "sanity check of DeploymentPos failed (TESTDUMMY not valid)");
|
|
18
|
-
static_assert(!ValidDeployment(Consensus::MAX_VERSION_BITS_DEPLOYMENTS), "sanity check of DeploymentPos failed (MAX value considered valid)");
|
|
19
|
-
static_assert(!ValidDeployment(static_cast<Consensus::BuriedDeployment>(Consensus::DEPLOYMENT_TESTDUMMY)), "sanity check of BuriedDeployment failed (overlaps with DeploymentPos)");
|
|
20
|
-
|
|
21
|
-
/* ValidDeployment only checks upper bounds for ensuring validity.
|
|
22
|
-
* This checks that the lowest possible value or the type is also a
|
|
23
|
-
* (specific) valid deployment so that lower bounds don't need to be checked.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
template<typename T, T x>
|
|
27
|
-
static constexpr bool is_minimum()
|
|
28
|
-
{
|
|
29
|
-
using U = typename std::underlying_type<T>::type;
|
|
30
|
-
return x == std::numeric_limits<U>::min();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static_assert(is_minimum<Consensus::BuriedDeployment, Consensus::DEPLOYMENT_HEIGHTINCB>(), "heightincb is not minimum value for BuriedDeployment");
|
|
34
|
-
static_assert(is_minimum<Consensus::DeploymentPos, Consensus::DEPLOYMENT_TESTDUMMY>(), "testdummy is not minimum value for DeploymentPos");
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2020-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#ifndef BITCOIN_DEPLOYMENTSTATUS_H
|
|
6
|
-
#define BITCOIN_DEPLOYMENTSTATUS_H
|
|
7
|
-
|
|
8
|
-
#include <chain.h>
|
|
9
|
-
#include <versionbits.h>
|
|
10
|
-
|
|
11
|
-
#include <limits>
|
|
12
|
-
|
|
13
|
-
/** Global cache for versionbits deployment status */
|
|
14
|
-
extern VersionBitsCache g_versionbitscache;
|
|
15
|
-
|
|
16
|
-
/** Determine if a deployment is active for the next block */
|
|
17
|
-
inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::BuriedDeployment dep)
|
|
18
|
-
{
|
|
19
|
-
assert(Consensus::ValidDeployment(dep));
|
|
20
|
-
return (pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1) >= params.DeploymentHeight(dep);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos dep)
|
|
24
|
-
{
|
|
25
|
-
assert(Consensus::ValidDeployment(dep));
|
|
26
|
-
return ThresholdState::ACTIVE == g_versionbitscache.State(pindexPrev, params, dep);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Determine if a deployment is active for this block */
|
|
30
|
-
inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::BuriedDeployment dep)
|
|
31
|
-
{
|
|
32
|
-
assert(Consensus::ValidDeployment(dep));
|
|
33
|
-
return index.nHeight >= params.DeploymentHeight(dep);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::DeploymentPos dep)
|
|
37
|
-
{
|
|
38
|
-
assert(Consensus::ValidDeployment(dep));
|
|
39
|
-
return DeploymentActiveAfter(index.pprev, params, dep);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Determine if a deployment is enabled (can ever be active) */
|
|
43
|
-
inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::BuriedDeployment dep)
|
|
44
|
-
{
|
|
45
|
-
assert(Consensus::ValidDeployment(dep));
|
|
46
|
-
return params.DeploymentHeight(dep) != std::numeric_limits<int>::max();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::DeploymentPos dep)
|
|
50
|
-
{
|
|
51
|
-
assert(Consensus::ValidDeployment(dep));
|
|
52
|
-
return params.vDeployments[dep].nStartTime != Consensus::BIP9Deployment::NEVER_ACTIVE;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
#endif // BITCOIN_DEPLOYMENTSTATUS_H
|
|
@@ -35,7 +35,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
|
|
|
35
35
|
"-fallbackfee=<amt>",
|
|
36
36
|
"-keypool=<n>",
|
|
37
37
|
"-maxapsfee=<n>",
|
|
38
|
-
"-maxtxfee=<amt>",
|
|
39
38
|
"-mintxfee=<amt>",
|
|
40
39
|
"-paytxfee=<amt>",
|
|
41
40
|
"-signer=<cmd>",
|
|
@@ -45,7 +44,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
|
|
|
45
44
|
"-walletbroadcast",
|
|
46
45
|
"-walletdir=<dir>",
|
|
47
46
|
"-walletnotify=<cmd>",
|
|
48
|
-
"-walletrbf",
|
|
49
47
|
"-dblogsize=<n>",
|
|
50
48
|
"-flushwallet",
|
|
51
49
|
"-privdb",
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields *
|
|
3
|
+
* Distributed under the MIT software license, see the accompanying *
|
|
4
|
+
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
|
5
|
+
***********************************************************************/
|
|
6
|
+
|
|
7
|
+
#include <inttypes.h>
|
|
8
|
+
#include <stdio.h>
|
|
9
|
+
|
|
10
|
+
#include "../include/secp256k1.h"
|
|
11
|
+
#include "assumptions.h"
|
|
12
|
+
#include "util.h"
|
|
13
|
+
#include "group.h"
|
|
14
|
+
#include "ecmult_gen.h"
|
|
15
|
+
#include "ecmult_gen_prec_impl.h"
|
|
16
|
+
|
|
17
|
+
int main(int argc, char **argv) {
|
|
18
|
+
const char outfile[] = "src/ecmult_gen_static_prec_table.h";
|
|
19
|
+
FILE* fp;
|
|
20
|
+
int bits;
|
|
21
|
+
|
|
22
|
+
(void)argc;
|
|
23
|
+
(void)argv;
|
|
24
|
+
|
|
25
|
+
fp = fopen(outfile, "w");
|
|
26
|
+
if (fp == NULL) {
|
|
27
|
+
fprintf(stderr, "Could not open %s for writing!\n", outfile);
|
|
28
|
+
return -1;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fprintf(fp, "/* This file was automatically generated by gen_ecmult_gen_static_prec_table. */\n");
|
|
32
|
+
fprintf(fp, "/* See ecmult_gen_impl.h for details about the contents of this file. */\n");
|
|
33
|
+
fprintf(fp, "#ifndef SECP256K1_ECMULT_GEN_STATIC_PREC_TABLE_H\n");
|
|
34
|
+
fprintf(fp, "#define SECP256K1_ECMULT_GEN_STATIC_PREC_TABLE_H\n");
|
|
35
|
+
|
|
36
|
+
fprintf(fp, "#include \"group.h\"\n");
|
|
37
|
+
|
|
38
|
+
fprintf(fp, "#define S(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) "
|
|
39
|
+
"SECP256K1_GE_STORAGE_CONST(0x##a##u,0x##b##u,0x##c##u,0x##d##u,0x##e##u,0x##f##u,0x##g##u,"
|
|
40
|
+
"0x##h##u,0x##i##u,0x##j##u,0x##k##u,0x##l##u,0x##m##u,0x##n##u,0x##o##u,0x##p##u)\n");
|
|
41
|
+
|
|
42
|
+
fprintf(fp, "#ifdef EXHAUSTIVE_TEST_ORDER\n");
|
|
43
|
+
fprintf(fp, "static secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[ECMULT_GEN_PREC_N(ECMULT_GEN_PREC_BITS)][ECMULT_GEN_PREC_G(ECMULT_GEN_PREC_BITS)];\n");
|
|
44
|
+
fprintf(fp, "#else\n");
|
|
45
|
+
fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[ECMULT_GEN_PREC_N(ECMULT_GEN_PREC_BITS)][ECMULT_GEN_PREC_G(ECMULT_GEN_PREC_BITS)] = {\n");
|
|
46
|
+
|
|
47
|
+
for (bits = 2; bits <= 8; bits *= 2) {
|
|
48
|
+
int g = ECMULT_GEN_PREC_G(bits);
|
|
49
|
+
int n = ECMULT_GEN_PREC_N(bits);
|
|
50
|
+
int inner, outer;
|
|
51
|
+
|
|
52
|
+
secp256k1_ge_storage* table = checked_malloc(&default_error_callback, n * g * sizeof(secp256k1_ge_storage));
|
|
53
|
+
secp256k1_ecmult_gen_create_prec_table(table, &secp256k1_ge_const_g, bits);
|
|
54
|
+
|
|
55
|
+
fprintf(fp, "#if ECMULT_GEN_PREC_BITS == %d\n", bits);
|
|
56
|
+
for(outer = 0; outer != n; outer++) {
|
|
57
|
+
fprintf(fp,"{");
|
|
58
|
+
for(inner = 0; inner != g; inner++) {
|
|
59
|
+
fprintf(fp, "S(%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32
|
|
60
|
+
",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32")",
|
|
61
|
+
SECP256K1_GE_STORAGE_CONST_GET(table[outer * g + inner]));
|
|
62
|
+
if (inner != g - 1) {
|
|
63
|
+
fprintf(fp,",\n");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (outer != n - 1) {
|
|
67
|
+
fprintf(fp,"},\n");
|
|
68
|
+
} else {
|
|
69
|
+
fprintf(fp,"}\n");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
fprintf(fp, "#endif\n");
|
|
73
|
+
free(table);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fprintf(fp, "};\n");
|
|
77
|
+
fprintf(fp, "#endif /* EXHAUSTIVE_TEST_ORDER */\n");
|
|
78
|
+
fprintf(fp, "#undef SC\n");
|
|
79
|
+
fprintf(fp, "#endif /* SECP256K1_ECMULT_GEN_STATIC_PREC_TABLE_H */\n");
|
|
80
|
+
fclose(fp);
|
|
81
|
+
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/*****************************************************************************************************
|
|
2
|
+
* Copyright (c) 2013, 2014, 2017, 2021 Pieter Wuille, Andrew Poelstra, Jonas Nick, Russell O'Connor *
|
|
3
|
+
* Distributed under the MIT software license, see the accompanying *
|
|
4
|
+
* file COPYING or https://www.opensource.org/licenses/mit-license.php. *
|
|
5
|
+
*****************************************************************************************************/
|
|
6
|
+
|
|
7
|
+
#include <inttypes.h>
|
|
8
|
+
#include <stdio.h>
|
|
9
|
+
|
|
10
|
+
/* Autotools creates libsecp256k1-config.h, of which ECMULT_WINDOW_SIZE is needed.
|
|
11
|
+
ifndef guard so downstream users can define their own if they do not use autotools. */
|
|
12
|
+
#if !defined(ECMULT_WINDOW_SIZE)
|
|
13
|
+
#include "libsecp256k1-config.h"
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
#include "../include/secp256k1.h"
|
|
17
|
+
#include "assumptions.h"
|
|
18
|
+
#include "util.h"
|
|
19
|
+
#include "field_impl.h"
|
|
20
|
+
#include "group_impl.h"
|
|
21
|
+
#include "ecmult.h"
|
|
22
|
+
|
|
23
|
+
void print_table(FILE *fp, const char *name, int window_g, const secp256k1_gej *gen, int with_conditionals) {
|
|
24
|
+
static secp256k1_gej gj;
|
|
25
|
+
static secp256k1_ge ge, dgen;
|
|
26
|
+
static secp256k1_ge_storage ges;
|
|
27
|
+
int j;
|
|
28
|
+
int i;
|
|
29
|
+
|
|
30
|
+
gj = *gen;
|
|
31
|
+
secp256k1_ge_set_gej_var(&ge, &gj);
|
|
32
|
+
secp256k1_ge_to_storage(&ges, &ge);
|
|
33
|
+
|
|
34
|
+
fprintf(fp, "static const secp256k1_ge_storage %s[ECMULT_TABLE_SIZE(WINDOW_G)] = {\n", name);
|
|
35
|
+
fprintf(fp, " S(%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32
|
|
36
|
+
",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32")\n",
|
|
37
|
+
SECP256K1_GE_STORAGE_CONST_GET(ges));
|
|
38
|
+
|
|
39
|
+
secp256k1_gej_double_var(&gj, gen, NULL);
|
|
40
|
+
secp256k1_ge_set_gej_var(&dgen, &gj);
|
|
41
|
+
|
|
42
|
+
j = 1;
|
|
43
|
+
for(i = 3; i <= window_g; ++i) {
|
|
44
|
+
if (with_conditionals) {
|
|
45
|
+
fprintf(fp, "#if ECMULT_TABLE_SIZE(WINDOW_G) > %ld\n", ECMULT_TABLE_SIZE(i-1));
|
|
46
|
+
}
|
|
47
|
+
for(;j < ECMULT_TABLE_SIZE(i); ++j) {
|
|
48
|
+
secp256k1_gej_set_ge(&gj, &ge);
|
|
49
|
+
secp256k1_gej_add_ge_var(&gj, &gj, &dgen, NULL);
|
|
50
|
+
secp256k1_ge_set_gej_var(&ge, &gj);
|
|
51
|
+
secp256k1_ge_to_storage(&ges, &ge);
|
|
52
|
+
|
|
53
|
+
fprintf(fp, ",S(%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32
|
|
54
|
+
",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32")\n",
|
|
55
|
+
SECP256K1_GE_STORAGE_CONST_GET(ges));
|
|
56
|
+
}
|
|
57
|
+
if (with_conditionals) {
|
|
58
|
+
fprintf(fp, "#endif\n");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
fprintf(fp, "};\n");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
void print_two_tables(FILE *fp, int window_g, const secp256k1_ge *g, int with_conditionals) {
|
|
65
|
+
secp256k1_gej gj;
|
|
66
|
+
int i;
|
|
67
|
+
|
|
68
|
+
secp256k1_gej_set_ge(&gj, g);
|
|
69
|
+
print_table(fp, "secp256k1_pre_g", window_g, &gj, with_conditionals);
|
|
70
|
+
for (i = 0; i < 128; ++i) {
|
|
71
|
+
secp256k1_gej_double_var(&gj, &gj, NULL);
|
|
72
|
+
}
|
|
73
|
+
print_table(fp, "secp256k1_pre_g_128", window_g, &gj, with_conditionals);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
int main(void) {
|
|
77
|
+
const secp256k1_ge g = SECP256K1_G;
|
|
78
|
+
const secp256k1_ge g_13 = SECP256K1_G_ORDER_13;
|
|
79
|
+
const secp256k1_ge g_199 = SECP256K1_G_ORDER_199;
|
|
80
|
+
const int window_g_13 = 4;
|
|
81
|
+
const int window_g_199 = 8;
|
|
82
|
+
FILE* fp;
|
|
83
|
+
|
|
84
|
+
fp = fopen("src/ecmult_static_pre_g.h","w");
|
|
85
|
+
if (fp == NULL) {
|
|
86
|
+
fprintf(stderr, "Could not open src/ecmult_static_pre_g.h for writing!\n");
|
|
87
|
+
return -1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fprintf(fp, "/* This file was automatically generated by gen_ecmult_static_pre_g. */\n");
|
|
91
|
+
fprintf(fp, "/* This file contains an array secp256k1_pre_g with odd multiples of the base point G and\n");
|
|
92
|
+
fprintf(fp, " * an array secp256k1_pre_g_128 with odd multiples of 2^128*G for accelerating the computation of a*P + b*G.\n");
|
|
93
|
+
fprintf(fp, " */\n");
|
|
94
|
+
fprintf(fp, "#ifndef SECP256K1_ECMULT_STATIC_PRE_G_H\n");
|
|
95
|
+
fprintf(fp, "#define SECP256K1_ECMULT_STATIC_PRE_G_H\n");
|
|
96
|
+
fprintf(fp, "#include \"group.h\"\n");
|
|
97
|
+
fprintf(fp, "#ifdef S\n");
|
|
98
|
+
fprintf(fp, " #error macro identifier S already in use.\n");
|
|
99
|
+
fprintf(fp, "#endif\n");
|
|
100
|
+
fprintf(fp, "#define S(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) "
|
|
101
|
+
"SECP256K1_GE_STORAGE_CONST(0x##a##u,0x##b##u,0x##c##u,0x##d##u,0x##e##u,0x##f##u,0x##g##u,"
|
|
102
|
+
"0x##h##u,0x##i##u,0x##j##u,0x##k##u,0x##l##u,0x##m##u,0x##n##u,0x##o##u,0x##p##u)\n");
|
|
103
|
+
fprintf(fp, "#if ECMULT_TABLE_SIZE(ECMULT_WINDOW_SIZE) > %ld\n", ECMULT_TABLE_SIZE(ECMULT_WINDOW_SIZE));
|
|
104
|
+
fprintf(fp, " #error configuration mismatch, invalid ECMULT_WINDOW_SIZE. Try deleting ecmult_static_pre_g.h before the build.\n");
|
|
105
|
+
fprintf(fp, "#endif\n");
|
|
106
|
+
fprintf(fp, "#if defined(EXHAUSTIVE_TEST_ORDER)\n");
|
|
107
|
+
fprintf(fp, "#if EXHAUSTIVE_TEST_ORDER == 13\n");
|
|
108
|
+
fprintf(fp, "#define WINDOW_G %d\n", window_g_13);
|
|
109
|
+
|
|
110
|
+
print_two_tables(fp, window_g_13, &g_13, 0);
|
|
111
|
+
|
|
112
|
+
fprintf(fp, "#elif EXHAUSTIVE_TEST_ORDER == 199\n");
|
|
113
|
+
fprintf(fp, "#define WINDOW_G %d\n", window_g_199);
|
|
114
|
+
|
|
115
|
+
print_two_tables(fp, window_g_199, &g_199, 0);
|
|
116
|
+
|
|
117
|
+
fprintf(fp, "#else\n");
|
|
118
|
+
fprintf(fp, " #error No known generator for the specified exhaustive test group order.\n");
|
|
119
|
+
fprintf(fp, "#endif\n");
|
|
120
|
+
fprintf(fp, "#else /* !defined(EXHAUSTIVE_TEST_ORDER) */\n");
|
|
121
|
+
fprintf(fp, "#define WINDOW_G ECMULT_WINDOW_SIZE\n");
|
|
122
|
+
|
|
123
|
+
print_two_tables(fp, ECMULT_WINDOW_SIZE, &g, 1);
|
|
124
|
+
|
|
125
|
+
fprintf(fp, "#endif\n");
|
|
126
|
+
fprintf(fp, "#undef S\n");
|
|
127
|
+
fprintf(fp, "#endif\n");
|
|
128
|
+
fclose(fp);
|
|
129
|
+
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
@@ -94,3 +94,12 @@ CHashWriter TaggedHash(const std::string& tag)
|
|
|
94
94
|
writer << taghash << taghash;
|
|
95
95
|
return writer;
|
|
96
96
|
}
|
|
97
|
+
|
|
98
|
+
int32_t peercoinRandseed;
|
|
99
|
+
int univHash(const uint256 &x) {
|
|
100
|
+
int h = peercoinRandseed >> 20;
|
|
101
|
+
const uint32_t *p = x.GetDataPtr();
|
|
102
|
+
for(int i = 0; i < 8; i++)
|
|
103
|
+
h ^= (p[i] >> (h & 0xf)) + (peercoinRandseed >> i);
|
|
104
|
+
return (h + (h >> 16)) & 1023; // 2^n - 1
|
|
105
|
+
}
|
|
@@ -212,4 +212,7 @@ void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char he
|
|
|
212
212
|
*/
|
|
213
213
|
CHashWriter TaggedHash(const std::string& tag);
|
|
214
214
|
|
|
215
|
+
extern int32_t peercoinRandseed;
|
|
216
|
+
int univHash(const uint256 &x);
|
|
217
|
+
|
|
215
218
|
#endif // BITCOIN_HASH_H
|
|
@@ -105,7 +105,7 @@ bool BaseIndex::Init()
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
if (prune_violation) {
|
|
108
|
-
return InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), GetName()));
|
|
108
|
+
return InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), GetName())); // peercoin: should never happen
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
return true;
|
|
@@ -112,8 +112,8 @@ CoinStatsIndex::CoinStatsIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
|
|
|
112
112
|
bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|
113
113
|
{
|
|
114
114
|
CBlockUndo block_undo;
|
|
115
|
-
const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
|
|
116
|
-
m_total_subsidy += block_subsidy;
|
|
115
|
+
//const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
|
|
116
|
+
//m_total_subsidy += block_subsidy;
|
|
117
117
|
|
|
118
118
|
// Ignore genesis block
|
|
119
119
|
if (pindex->nHeight > 0) {
|
|
@@ -147,14 +147,14 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|
|
147
147
|
|
|
148
148
|
// Skip duplicate txid coinbase transactions (BIP30).
|
|
149
149
|
if (is_bip30_block && tx->IsCoinBase()) {
|
|
150
|
-
m_total_unspendable_amount += block_subsidy;
|
|
151
|
-
m_total_unspendables_bip30 += block_subsidy;
|
|
150
|
+
//m_total_unspendable_amount += block_subsidy;
|
|
151
|
+
//m_total_unspendables_bip30 += block_subsidy;
|
|
152
152
|
continue;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
for (uint32_t j = 0; j < tx->vout.size(); ++j) {
|
|
156
156
|
const CTxOut& out{tx->vout[j]};
|
|
157
|
-
Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
|
|
157
|
+
Coin coin{out, pindex->nHeight, tx->IsCoinBase(), tx->IsCoinStake(), (int)tx->nTime};
|
|
158
158
|
COutPoint outpoint{tx->GetHash(), j};
|
|
159
159
|
|
|
160
160
|
// Skip unspendable coins
|
|
@@ -197,17 +197,17 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|
|
197
197
|
}
|
|
198
198
|
} else {
|
|
199
199
|
// genesis block
|
|
200
|
-
m_total_unspendable_amount += block_subsidy;
|
|
201
|
-
m_total_unspendables_genesis_block += block_subsidy;
|
|
200
|
+
//m_total_unspendable_amount += block_subsidy;
|
|
201
|
+
//m_total_unspendables_genesis_block += block_subsidy;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
// If spent prevouts + block subsidy are still a higher amount than
|
|
205
205
|
// new outputs + coinbase + current unspendable amount this means
|
|
206
206
|
// the miner did not claim the full block reward. Unclaimed block
|
|
207
207
|
// rewards are also unspendable.
|
|
208
|
-
const CAmount unclaimed_rewards{(m_total_prevout_spent_amount + m_total_subsidy) - (m_total_new_outputs_ex_coinbase_amount + m_total_coinbase_amount + m_total_unspendable_amount)};
|
|
209
|
-
m_total_unspendable_amount += unclaimed_rewards;
|
|
210
|
-
m_total_unspendables_unclaimed_rewards += unclaimed_rewards;
|
|
208
|
+
//const CAmount unclaimed_rewards{(m_total_prevout_spent_amount + m_total_subsidy) - (m_total_new_outputs_ex_coinbase_amount + m_total_coinbase_amount + m_total_unspendable_amount)};
|
|
209
|
+
//m_total_unspendable_amount += unclaimed_rewards;
|
|
210
|
+
//m_total_unspendables_unclaimed_rewards += unclaimed_rewards;
|
|
211
211
|
|
|
212
212
|
std::pair<uint256, DBVal> value;
|
|
213
213
|
value.first = pindex->GetBlockHash();
|
|
@@ -215,14 +215,14 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|
|
215
215
|
value.second.bogo_size = m_bogo_size;
|
|
216
216
|
value.second.total_amount = m_total_amount;
|
|
217
217
|
value.second.total_subsidy = m_total_subsidy;
|
|
218
|
-
value.second.total_unspendable_amount = m_total_unspendable_amount;
|
|
218
|
+
//value.second.total_unspendable_amount = m_total_unspendable_amount;
|
|
219
219
|
value.second.total_prevout_spent_amount = m_total_prevout_spent_amount;
|
|
220
220
|
value.second.total_new_outputs_ex_coinbase_amount = m_total_new_outputs_ex_coinbase_amount;
|
|
221
221
|
value.second.total_coinbase_amount = m_total_coinbase_amount;
|
|
222
|
-
value.second.total_unspendables_genesis_block = m_total_unspendables_genesis_block;
|
|
223
|
-
value.second.total_unspendables_bip30 = m_total_unspendables_bip30;
|
|
222
|
+
//value.second.total_unspendables_genesis_block = m_total_unspendables_genesis_block;
|
|
223
|
+
//value.second.total_unspendables_bip30 = m_total_unspendables_bip30;
|
|
224
224
|
value.second.total_unspendables_scripts = m_total_unspendables_scripts;
|
|
225
|
-
value.second.total_unspendables_unclaimed_rewards = m_total_unspendables_unclaimed_rewards;
|
|
225
|
+
//value.second.total_unspendables_unclaimed_rewards = m_total_unspendables_unclaimed_rewards;
|
|
226
226
|
|
|
227
227
|
uint256 out;
|
|
228
228
|
m_muhash.Finalize(out);
|
|
@@ -374,15 +374,15 @@ bool CoinStatsIndex::Init()
|
|
|
374
374
|
m_transaction_output_count = entry.transaction_output_count;
|
|
375
375
|
m_bogo_size = entry.bogo_size;
|
|
376
376
|
m_total_amount = entry.total_amount;
|
|
377
|
-
m_total_subsidy = entry.total_subsidy;
|
|
378
|
-
m_total_unspendable_amount = entry.total_unspendable_amount;
|
|
377
|
+
//m_total_subsidy = entry.total_subsidy;
|
|
378
|
+
//m_total_unspendable_amount = entry.total_unspendable_amount;
|
|
379
379
|
m_total_prevout_spent_amount = entry.total_prevout_spent_amount;
|
|
380
380
|
m_total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
|
|
381
381
|
m_total_coinbase_amount = entry.total_coinbase_amount;
|
|
382
|
-
m_total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
|
|
383
|
-
m_total_unspendables_bip30 = entry.total_unspendables_bip30;
|
|
382
|
+
//m_total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
|
|
383
|
+
//m_total_unspendables_bip30 = entry.total_unspendables_bip30;
|
|
384
384
|
m_total_unspendables_scripts = entry.total_unspendables_scripts;
|
|
385
|
-
m_total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
|
|
385
|
+
//m_total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
return true;
|
|
@@ -394,8 +394,8 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
|
|
|
394
394
|
CBlockUndo block_undo;
|
|
395
395
|
std::pair<uint256, DBVal> read_out;
|
|
396
396
|
|
|
397
|
-
const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
|
|
398
|
-
m_total_subsidy -= block_subsidy;
|
|
397
|
+
//const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
|
|
398
|
+
//m_total_subsidy -= block_subsidy;
|
|
399
399
|
|
|
400
400
|
// Ignore genesis block
|
|
401
401
|
if (pindex->nHeight > 0) {
|
|
@@ -426,7 +426,7 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
|
|
|
426
426
|
for (uint32_t j = 0; j < tx->vout.size(); ++j) {
|
|
427
427
|
const CTxOut& out{tx->vout[j]};
|
|
428
428
|
COutPoint outpoint{tx->GetHash(), j};
|
|
429
|
-
Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
|
|
429
|
+
Coin coin{out, pindex->nHeight, tx->IsCoinBase(), tx->IsCoinStake(), (int)tx->nTime};
|
|
430
430
|
|
|
431
431
|
// Skip unspendable coins
|
|
432
432
|
if (coin.out.scriptPubKey.IsUnspendable()) {
|
|
@@ -100,3 +100,8 @@ bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRe
|
|
|
100
100
|
block_hash = header.GetHash();
|
|
101
101
|
return true;
|
|
102
102
|
}
|
|
103
|
+
|
|
104
|
+
bool TxIndex::FindTxPosition(const uint256& txid, CDiskTxPos& pos) const
|
|
105
|
+
{
|
|
106
|
+
return m_db->ReadTxPos(txid, pos);
|
|
107
|
+
}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
#define BITCOIN_INDEX_TXINDEX_H
|
|
7
7
|
|
|
8
8
|
#include <index/base.h>
|
|
9
|
+
#include <index/disktxpos.h>
|
|
10
|
+
#include <primitives/block.h>
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* TxIndex is used to look up transactions included in the blockchain by hash.
|
|
@@ -41,6 +43,9 @@ public:
|
|
|
41
43
|
/// @param[out] tx The transaction itself.
|
|
42
44
|
/// @return true if transaction is found, false otherwise
|
|
43
45
|
bool FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const;
|
|
46
|
+
|
|
47
|
+
bool FindTxPosition(const uint256& txid, CDiskTxPos& pos) const;
|
|
48
|
+
std::map<uint256,std::pair<CBlockHeader,CTransactionRef>> cachedTxs;
|
|
44
49
|
};
|
|
45
50
|
|
|
46
51
|
/// The global transaction index, used in GetTransaction. May be null.
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
#include <chainparams.h>
|
|
17
17
|
#include <compat/sanity.h>
|
|
18
18
|
#include <consensus/amount.h>
|
|
19
|
-
#include <deploymentstatus.h>
|
|
20
19
|
#include <fs.h>
|
|
21
20
|
#include <hash.h>
|
|
22
21
|
#include <httprpc.h>
|
|
@@ -39,8 +38,6 @@
|
|
|
39
38
|
#include <node/context.h>
|
|
40
39
|
#include <node/miner.h>
|
|
41
40
|
#include <node/ui_interface.h>
|
|
42
|
-
#include <policy/feerate.h>
|
|
43
|
-
#include <policy/fees.h>
|
|
44
41
|
#include <policy/policy.h>
|
|
45
42
|
#include <policy/settings.h>
|
|
46
43
|
#include <protocol.h>
|
|
@@ -62,13 +59,13 @@
|
|
|
62
59
|
#include <util/check.h>
|
|
63
60
|
#include <util/moneystr.h>
|
|
64
61
|
#include <util/strencodings.h>
|
|
65
|
-
#include <util/string.h>
|
|
66
62
|
#include <util/syscall_sandbox.h>
|
|
67
63
|
#include <util/system.h>
|
|
68
64
|
#include <util/thread.h>
|
|
69
65
|
#include <util/threadnames.h>
|
|
70
66
|
#include <util/translation.h>
|
|
71
67
|
#include <validation.h>
|
|
68
|
+
|
|
72
69
|
#include <validationinterface.h>
|
|
73
70
|
#include <walletinitinterface.h>
|
|
74
71
|
|
|
@@ -102,17 +99,14 @@ using node::CacheSizes;
|
|
|
102
99
|
using node::CalculateCacheSizes;
|
|
103
100
|
using node::ChainstateLoadVerifyError;
|
|
104
101
|
using node::ChainstateLoadingError;
|
|
105
|
-
using node::CleanupBlockRevFiles;
|
|
106
102
|
using node::DEFAULT_PRINTPRIORITY;
|
|
107
103
|
using node::DEFAULT_STOPAFTERBLOCKIMPORT;
|
|
108
104
|
using node::LoadChainstate;
|
|
109
105
|
using node::NodeContext;
|
|
110
106
|
using node::ThreadImport;
|
|
111
107
|
using node::VerifyLoadedChainstate;
|
|
112
|
-
using node::fHavePruned;
|
|
113
|
-
using node::fPruneMode;
|
|
114
108
|
using node::fReindex;
|
|
115
|
-
using
|
|
109
|
+
using interfaces::WalletLoader;
|
|
116
110
|
|
|
117
111
|
static const bool DEFAULT_PROXYRANDOMIZE = true;
|
|
118
112
|
static const bool DEFAULT_REST_ENABLE = false;
|
|
@@ -131,7 +125,9 @@ static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map";
|
|
|
131
125
|
/**
|
|
132
126
|
* The PID file facilities.
|
|
133
127
|
*/
|
|
134
|
-
static const char* BITCOIN_PID_FILENAME = "
|
|
128
|
+
static const char* BITCOIN_PID_FILENAME = "peercoind.pid";
|
|
129
|
+
|
|
130
|
+
static std::shared_ptr<CWallet> walletTmp;
|
|
135
131
|
|
|
136
132
|
static fs::path GetPidFile(const ArgsManager& args)
|
|
137
133
|
{
|
|
@@ -245,8 +241,6 @@ void Shutdown(NodeContext& node)
|
|
|
245
241
|
DumpMempool(*node.mempool);
|
|
246
242
|
}
|
|
247
243
|
|
|
248
|
-
// Drop transactions we were still watching, and record fee estimations.
|
|
249
|
-
if (node.fee_estimator) node.fee_estimator->Flush();
|
|
250
244
|
|
|
251
245
|
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
|
|
252
246
|
if (node.chainman) {
|
|
@@ -306,7 +300,6 @@ void Shutdown(NodeContext& node)
|
|
|
306
300
|
GetMainSignals().UnregisterBackgroundSignalScheduler();
|
|
307
301
|
init::UnsetGlobals();
|
|
308
302
|
node.mempool.reset();
|
|
309
|
-
node.fee_estimator.reset();
|
|
310
303
|
node.chainman.reset();
|
|
311
304
|
node.scheduler.reset();
|
|
312
305
|
|
|
@@ -420,9 +413,6 @@ void SetupServerArgs(ArgsManager& argsman)
|
|
|
420
413
|
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
421
414
|
argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
422
415
|
argsman.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
423
|
-
argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -coinstatsindex. "
|
|
424
|
-
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
|
|
425
|
-
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
426
416
|
argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
427
417
|
argsman.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
428
418
|
argsman.AddArg("-settings=<file>", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
@@ -549,19 +539,15 @@ void SetupServerArgs(ArgsManager& argsman)
|
|
|
549
539
|
SetupChainParamsBaseOptions(argsman);
|
|
550
540
|
|
|
551
541
|
argsman.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
|
|
552
|
-
argsman.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
|
|
553
|
-
argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
|
|
554
542
|
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
555
543
|
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
556
544
|
argsman.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
557
|
-
|
|
558
|
-
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
545
|
+
|
|
559
546
|
argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
560
547
|
argsman.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted inbound peers with default permissions. This will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
561
548
|
|
|
562
549
|
|
|
563
550
|
argsman.AddArg("-blockmaxweight=<n>", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
|
|
564
|
-
argsman.AddArg("-blockmintxfee=<amt>", strprintf("Set lowest fee rate (in %s/kvB) for transactions to be included in block creation. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
|
|
565
551
|
argsman.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::BLOCK_CREATION);
|
|
566
552
|
|
|
567
553
|
argsman.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
|
|
@@ -579,6 +565,7 @@ void SetupServerArgs(ArgsManager& argsman)
|
|
|
579
565
|
argsman.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
|
|
580
566
|
argsman.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
|
|
581
567
|
argsman.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
|
|
568
|
+
gArgs.AddArg("-nominting", "Disable minting of POS blocks", ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
|
|
582
569
|
|
|
583
570
|
#if HAVE_DECL_FORK
|
|
584
571
|
argsman.AddArg("-daemon", strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
@@ -592,6 +579,15 @@ void SetupServerArgs(ArgsManager& argsman)
|
|
|
592
579
|
argsman.AddArg("-sandbox=<mode>", "Use the experimental syscall sandbox in the specified mode (-sandbox=log-and-abort or -sandbox=abort). Allow only expected syscalls to be used by bitcoind. Note that this is an experimental new feature that may cause bitcoind to exit or crash unexpectedly: use with caution. In the \"log-and-abort\" mode the invocation of an unexpected syscall results in a debug handler being invoked which will log the incident and terminate the program (without executing the unexpected syscall). In the \"abort\" mode the invocation of an unexpected syscall results in the entire process being killed immediately by the kernel without executing the unexpected syscall.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
593
580
|
#endif // USE_SYSCALL_SANDBOX
|
|
594
581
|
|
|
582
|
+
// peercoin parameters
|
|
583
|
+
gArgs.AddArg("-printstakemodifier", "Print stakemodifier selection parameters if debug is enabled", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
|
584
|
+
gArgs.AddArg("-printcoinstake", "Print coinstake if debug is enabled", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
|
585
|
+
gArgs.AddArg("-printcoinage", "Print coinage if debug is enabled", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
|
586
|
+
gArgs.AddArg("-printcreation", "Print coin creation if debug is enabled", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
|
587
|
+
|
|
588
|
+
gArgs.AddArg("-reservebalance=<amt>", "Reserve this many coins", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
589
|
+
gArgs.AddArg("-minting", "Enable minting (default: true)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
590
|
+
|
|
595
591
|
// Add the hidden options
|
|
596
592
|
argsman.AddHiddenArgs(hidden_args);
|
|
597
593
|
}
|
|
@@ -853,14 +849,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|
|
853
849
|
nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS);
|
|
854
850
|
}
|
|
855
851
|
|
|
856
|
-
// if using block pruning, then disallow txindex and coinstatsindex
|
|
857
|
-
if (args.GetIntArg("-prune", 0)) {
|
|
858
|
-
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
|
|
859
|
-
return InitError(_("Prune mode is incompatible with -txindex."));
|
|
860
|
-
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX))
|
|
861
|
-
return InitError(_("Prune mode is incompatible with -coinstatsindex."));
|
|
862
|
-
}
|
|
863
|
-
|
|
864
852
|
// If -forcednsseed is set to true, ensure -dnsseed has not been set to false
|
|
865
853
|
if (args.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED) && !args.GetBoolArg("-dnsseed", DEFAULT_DNSSEED)){
|
|
866
854
|
return InitError(_("Cannot set -forcednsseed to true when setting -dnsseed to false."));
|
|
@@ -930,33 +918,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|
|
930
918
|
int64_t nMempoolSizeMin = args.GetIntArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
|
|
931
919
|
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
|
|
932
920
|
return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));
|
|
933
|
-
// incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool
|
|
934
|
-
// and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting.
|
|
935
|
-
if (args.IsArgSet("-incrementalrelayfee")) {
|
|
936
|
-
if (std::optional<CAmount> inc_relay_fee = ParseMoney(args.GetArg("-incrementalrelayfee", ""))) {
|
|
937
|
-
::incrementalRelayFee = CFeeRate{inc_relay_fee.value()};
|
|
938
|
-
} else {
|
|
939
|
-
return InitError(AmountErrMsg("incrementalrelayfee", args.GetArg("-incrementalrelayfee", "")));
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
|
|
944
|
-
int64_t nPruneArg = args.GetIntArg("-prune", 0);
|
|
945
|
-
if (nPruneArg < 0) {
|
|
946
|
-
return InitError(_("Prune cannot be configured with a negative value."));
|
|
947
|
-
}
|
|
948
|
-
nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024;
|
|
949
|
-
if (nPruneArg == 1) { // manual pruning: -prune=1
|
|
950
|
-
LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n");
|
|
951
|
-
nPruneTarget = std::numeric_limits<uint64_t>::max();
|
|
952
|
-
fPruneMode = true;
|
|
953
|
-
} else if (nPruneTarget) {
|
|
954
|
-
if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) {
|
|
955
|
-
return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
|
|
956
|
-
}
|
|
957
|
-
LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024);
|
|
958
|
-
fPruneMode = true;
|
|
959
|
-
}
|
|
960
921
|
|
|
961
922
|
nConnectTimeout = args.GetIntArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
|
|
962
923
|
if (nConnectTimeout <= 0) {
|
|
@@ -967,38 +928,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|
|
967
928
|
if (peer_connect_timeout <= 0) {
|
|
968
929
|
return InitError(Untranslated("peertimeout cannot be configured with a negative value."));
|
|
969
930
|
}
|
|
970
|
-
|
|
971
|
-
if (args.IsArgSet("-minrelaytxfee")) {
|
|
972
|
-
if (std::optional<CAmount> min_relay_fee = ParseMoney(args.GetArg("-minrelaytxfee", ""))) {
|
|
973
|
-
// High fee check is done afterward in CWallet::Create()
|
|
974
|
-
::minRelayTxFee = CFeeRate{min_relay_fee.value()};
|
|
975
|
-
} else {
|
|
976
|
-
return InitError(AmountErrMsg("minrelaytxfee", args.GetArg("-minrelaytxfee", "")));
|
|
977
|
-
}
|
|
978
|
-
} else if (incrementalRelayFee > ::minRelayTxFee) {
|
|
979
|
-
// Allow only setting incrementalRelayFee to control both
|
|
980
|
-
::minRelayTxFee = incrementalRelayFee;
|
|
981
|
-
LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n",::minRelayTxFee.ToString());
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
// Sanity check argument for min fee for including tx in block
|
|
985
|
-
// TODO: Harmonize which arguments need sanity checking and where that happens
|
|
986
|
-
if (args.IsArgSet("-blockmintxfee")) {
|
|
987
|
-
if (!ParseMoney(args.GetArg("-blockmintxfee", ""))) {
|
|
988
|
-
return InitError(AmountErrMsg("blockmintxfee", args.GetArg("-blockmintxfee", "")));
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
// Feerate used to define dust. Shouldn't be changed lightly as old
|
|
993
|
-
// implementations may inadvertently create non-standard transactions
|
|
994
|
-
if (args.IsArgSet("-dustrelayfee")) {
|
|
995
|
-
if (std::optional<CAmount> parsed = ParseMoney(args.GetArg("-dustrelayfee", ""))) {
|
|
996
|
-
dustRelayFee = CFeeRate{parsed.value()};
|
|
997
|
-
} else {
|
|
998
|
-
return InitError(AmountErrMsg("dustrelayfee", args.GetArg("-dustrelayfee", "")));
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
931
|
fRequireStandard = !args.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
|
|
1003
932
|
if (!chainparams.IsTestChain() && !fRequireStandard) {
|
|
1004
933
|
return InitError(strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.NetworkIDString()));
|
|
@@ -1024,7 +953,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|
|
1024
953
|
return InitError(Untranslated("Unknown rpcserialversion requested."));
|
|
1025
954
|
|
|
1026
955
|
nMaxTipAge = args.GetIntArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
|
|
1027
|
-
|
|
1028
956
|
if (args.IsArgSet("-proxy") && args.GetArg("-proxy", "").empty()) {
|
|
1029
957
|
return InitError(_("No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
|
|
1030
958
|
}
|
|
@@ -1065,7 +993,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|
|
1065
993
|
|
|
1066
994
|
static bool LockDataDirectory(bool probeOnly)
|
|
1067
995
|
{
|
|
1068
|
-
// Make sure only a single
|
|
996
|
+
// Make sure only a single Peercoin process is using the data directory.
|
|
1069
997
|
fs::path datadir = gArgs.GetDataDirNet();
|
|
1070
998
|
if (!DirIsWritable(datadir)) {
|
|
1071
999
|
return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), fs::PathToString(datadir)));
|
|
@@ -1082,6 +1010,8 @@ bool AppInitSanityChecks()
|
|
|
1082
1010
|
|
|
1083
1011
|
init::SetGlobals();
|
|
1084
1012
|
|
|
1013
|
+
// peercoin: init hash seed
|
|
1014
|
+
peercoinRandseed = GetRand(1 << 30);
|
|
1085
1015
|
if (!init::SanityChecks()) {
|
|
1086
1016
|
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), PACKAGE_NAME));
|
|
1087
1017
|
}
|
|
@@ -1135,9 +1065,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1135
1065
|
// Warn about relative -datadir path.
|
|
1136
1066
|
if (args.IsArgSet("-datadir") && !args.GetPathArg("-datadir").is_absolute()) {
|
|
1137
1067
|
LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the " /* Continued */
|
|
1138
|
-
"current working directory '%s'. This is fragile, because if
|
|
1068
|
+
"current working directory '%s'. This is fragile, because if peercoin is started in the future "
|
|
1139
1069
|
"from a different location, it will be unable to locate the current data files. There could "
|
|
1140
|
-
"also be data loss if
|
|
1070
|
+
"also be data loss if peercoin is started while in a temporary directory.\n",
|
|
1141
1071
|
args.GetArg("-datadir", ""), fs::PathToString(fs::current_path()));
|
|
1142
1072
|
}
|
|
1143
1073
|
|
|
@@ -1262,14 +1192,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1262
1192
|
assert(!node.connman);
|
|
1263
1193
|
node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), *node.addrman, args.GetBoolArg("-networkactive", true));
|
|
1264
1194
|
|
|
1265
|
-
assert(!node.fee_estimator);
|
|
1266
|
-
// Don't initialize fee estimation with old data if we don't relay transactions,
|
|
1267
|
-
// as they would never get updated.
|
|
1268
|
-
if (!ignores_incoming_txs) node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
|
|
1269
|
-
|
|
1270
1195
|
assert(!node.mempool);
|
|
1271
1196
|
int check_ratio = std::min<int>(std::max<int>(args.GetIntArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
|
|
1272
|
-
node.mempool = std::make_unique<CTxMemPool>(
|
|
1197
|
+
node.mempool = std::make_unique<CTxMemPool>(check_ratio);
|
|
1273
1198
|
|
|
1274
1199
|
assert(!node.chainman);
|
|
1275
1200
|
node.chainman = std::make_unique<ChainstateManager>();
|
|
@@ -1397,9 +1322,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1397
1322
|
int64_t nMempoolSizeMax = args.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
|
|
1398
1323
|
LogPrintf("Cache configuration:\n");
|
|
1399
1324
|
LogPrintf("* Using %.1f MiB for block index database\n", cache_sizes.block_tree_db * (1.0 / 1024 / 1024));
|
|
1400
|
-
|
|
1401
|
-
LogPrintf("* Using %.1f MiB for transaction index database\n", cache_sizes.tx_index * (1.0 / 1024 / 1024));
|
|
1402
|
-
}
|
|
1325
|
+
LogPrintf("* Using %.1f MiB for transaction index database\n", cache_sizes.tx_index * (1.0 / 1024 / 1024));
|
|
1403
1326
|
for (BlockFilterType filter_type : g_enabled_filter_types) {
|
|
1404
1327
|
LogPrintf("* Using %.1f MiB for %s block filter index database\n",
|
|
1405
1328
|
cache_sizes.filter_index * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
|
|
@@ -1419,7 +1342,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1419
1342
|
maybe_load_error = LoadChainstate(fReset,
|
|
1420
1343
|
chainman,
|
|
1421
1344
|
Assert(node.mempool.get()),
|
|
1422
|
-
fPruneMode,
|
|
1423
1345
|
chainparams.GetConsensus(),
|
|
1424
1346
|
fReindexChainState,
|
|
1425
1347
|
cache_sizes.block_tree_db,
|
|
@@ -1446,9 +1368,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1446
1368
|
// If the loaded chain has a wrong genesis, bail out immediately
|
|
1447
1369
|
// (we're likely using a testnet datadir, or the other way around).
|
|
1448
1370
|
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
|
|
1449
|
-
case ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX:
|
|
1450
|
-
strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain");
|
|
1451
|
-
break;
|
|
1452
1371
|
case ChainstateLoadingError::ERROR_LOAD_GENESIS_BLOCK_FAILED:
|
|
1453
1372
|
strLoadError = _("Error initializing block database");
|
|
1454
1373
|
break;
|
|
@@ -1476,10 +1395,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1476
1395
|
try {
|
|
1477
1396
|
uiInterface.InitMessage(_("Verifying blocks…").translated);
|
|
1478
1397
|
auto check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
|
|
1479
|
-
if (fHavePruned && check_blocks > MIN_BLOCKS_TO_KEEP) {
|
|
1480
|
-
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
|
|
1481
|
-
MIN_BLOCKS_TO_KEEP);
|
|
1482
|
-
}
|
|
1483
1398
|
maybe_verify_error = VerifyLoadedChainstate(chainman,
|
|
1484
1399
|
fReset,
|
|
1485
1400
|
fReindexChainState,
|
|
@@ -1540,15 +1455,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1540
1455
|
}
|
|
1541
1456
|
|
|
1542
1457
|
// ********************************************************* Step 8: start indexers
|
|
1543
|
-
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
|
|
1544
1458
|
if (const auto error{CheckLegacyTxindex(*Assert(chainman.m_blockman.m_block_tree_db))}) {
|
|
1545
1459
|
return InitError(*error);
|
|
1546
1460
|
}
|
|
1547
1461
|
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
}
|
|
1462
|
+
g_txindex = std::make_unique<TxIndex>(cache_sizes.tx_index, false, fReindex);
|
|
1463
|
+
if (!g_txindex->Start(chainman.ActiveChainstate())) {
|
|
1464
|
+
return false;
|
|
1552
1465
|
}
|
|
1553
1466
|
|
|
1554
1467
|
for (const auto& filter_type : g_enabled_filter_types) {
|
|
@@ -1574,19 +1487,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1574
1487
|
|
|
1575
1488
|
// ********************************************************* Step 10: data directory maintenance
|
|
1576
1489
|
|
|
1577
|
-
//
|
|
1578
|
-
// after
|
|
1579
|
-
|
|
1580
|
-
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
|
|
1581
|
-
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
|
|
1582
|
-
if (!fReindex) {
|
|
1583
|
-
LOCK(cs_main);
|
|
1584
|
-
for (CChainState* chainstate : chainman.GetAll()) {
|
|
1585
|
-
uiInterface.InitMessage(_("Pruning blockstore…").translated);
|
|
1586
|
-
chainstate->PruneAndFlush();
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1490
|
+
// Note that setting NODE_WITNESS is never required: the only downside from not
|
|
1491
|
+
// doing so is that after activation, no upgraded nodes will fetch from you.
|
|
1492
|
+
nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS);
|
|
1590
1493
|
|
|
1591
1494
|
// ********************************************************* Step 11: import blocks
|
|
1592
1495
|
|
|
@@ -1829,6 +1732,15 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1829
1732
|
|
|
1830
1733
|
#if HAVE_SYSTEM
|
|
1831
1734
|
StartupNotify(args);
|
|
1735
|
+
#endif
|
|
1736
|
+
#ifdef ENABLE_WALLET
|
|
1737
|
+
{
|
|
1738
|
+
// ppctodo: deal with multiple wallets
|
|
1739
|
+
if (node.wallet_loader->getWallets().size() && gArgs.GetBoolArg("-stakegen", true)) {
|
|
1740
|
+
walletTmp = std::shared_ptr<CWallet>(node.wallet_loader->getWallets()[0]->wallet());
|
|
1741
|
+
MintStake(walletTmp, node);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1832
1744
|
#endif
|
|
1833
1745
|
|
|
1834
1746
|
return true;
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**********************************************************************
|
|
2
|
+
* Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko *
|
|
3
|
+
* Distributed under the MIT software license, see the accompanying *
|
|
4
|
+
* file LICENSE or http://www.opensource.org/licenses/mit-license.php.*
|
|
5
|
+
**********************************************************************/
|
|
6
|
+
|
|
7
|
+
#ifndef _MINISKETCH_INT_UTILS_H_
|
|
8
|
+
#define _MINISKETCH_INT_UTILS_H_
|
|
9
|
+
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
|
|
12
|
+
#include <limits>
|
|
13
|
+
#include <algorithm>
|
|
14
|
+
#include <type_traits>
|
|
15
|
+
|
|
16
|
+
#ifdef _MSC_VER
|
|
17
|
+
# include <intrin.h>
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
template<int bits>
|
|
21
|
+
static constexpr inline uint64_t Rot(uint64_t x) { return (x << bits) | (x >> (64 - bits)); }
|
|
22
|
+
|
|
23
|
+
static inline void SipHashRound(uint64_t& v0, uint64_t& v1, uint64_t& v2, uint64_t& v3) {
|
|
24
|
+
v0 += v1; v1 = Rot<13>(v1); v1 ^= v0;
|
|
25
|
+
v0 = Rot<32>(v0);
|
|
26
|
+
v2 += v3; v3 = Rot<16>(v3); v3 ^= v2;
|
|
27
|
+
v0 += v3; v3 = Rot<21>(v3); v3 ^= v0;
|
|
28
|
+
v2 += v1; v1 = Rot<17>(v1); v1 ^= v2;
|
|
29
|
+
v2 = Rot<32>(v2);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
inline uint64_t SipHash(uint64_t k0, uint64_t k1, uint64_t data) {
|
|
33
|
+
uint64_t v0 = 0x736f6d6570736575ULL ^ k0;
|
|
34
|
+
uint64_t v1 = 0x646f72616e646f6dULL ^ k1;
|
|
35
|
+
uint64_t v2 = 0x6c7967656e657261ULL ^ k0;
|
|
36
|
+
uint64_t v3 = 0x7465646279746573ULL ^ k1 ^ data;
|
|
37
|
+
SipHashRound(v0, v1, v2, v3);
|
|
38
|
+
SipHashRound(v0, v1, v2, v3);
|
|
39
|
+
v0 ^= data;
|
|
40
|
+
v3 ^= 0x800000000000000ULL;
|
|
41
|
+
SipHashRound(v0, v1, v2, v3);
|
|
42
|
+
SipHashRound(v0, v1, v2, v3);
|
|
43
|
+
v0 ^= 0x800000000000000ULL;
|
|
44
|
+
v2 ^= 0xFF;
|
|
45
|
+
SipHashRound(v0, v1, v2, v3);
|
|
46
|
+
SipHashRound(v0, v1, v2, v3);
|
|
47
|
+
SipHashRound(v0, v1, v2, v3);
|
|
48
|
+
SipHashRound(v0, v1, v2, v3);
|
|
49
|
+
return v0 ^ v1 ^ v2 ^ v3;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class BitWriter {
|
|
53
|
+
unsigned char state = 0;
|
|
54
|
+
int offset = 0;
|
|
55
|
+
unsigned char* out;
|
|
56
|
+
|
|
57
|
+
public:
|
|
58
|
+
BitWriter(unsigned char* output) : out(output) {}
|
|
59
|
+
|
|
60
|
+
template<int BITS, typename I>
|
|
61
|
+
inline void Write(I val) {
|
|
62
|
+
int bits = BITS;
|
|
63
|
+
if (bits + offset >= 8) {
|
|
64
|
+
state |= ((val & ((I(1) << (8 - offset)) - 1)) << offset);
|
|
65
|
+
*(out++) = state;
|
|
66
|
+
val >>= (8 - offset);
|
|
67
|
+
bits -= 8 - offset;
|
|
68
|
+
offset = 0;
|
|
69
|
+
state = 0;
|
|
70
|
+
}
|
|
71
|
+
while (bits >= 8) {
|
|
72
|
+
*(out++) = val & 255;
|
|
73
|
+
val >>= 8;
|
|
74
|
+
bits -= 8;
|
|
75
|
+
}
|
|
76
|
+
state |= ((val & ((I(1) << bits) - 1)) << offset);
|
|
77
|
+
offset += bits;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
inline void Flush() {
|
|
81
|
+
if (offset) {
|
|
82
|
+
*(out++) = state;
|
|
83
|
+
state = 0;
|
|
84
|
+
offset = 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
class BitReader {
|
|
90
|
+
unsigned char state = 0;
|
|
91
|
+
int offset = 0;
|
|
92
|
+
const unsigned char* in;
|
|
93
|
+
|
|
94
|
+
public:
|
|
95
|
+
BitReader(const unsigned char* input) : in(input) {}
|
|
96
|
+
|
|
97
|
+
template<int BITS, typename I>
|
|
98
|
+
inline I Read() {
|
|
99
|
+
int bits = BITS;
|
|
100
|
+
if (offset >= bits) {
|
|
101
|
+
I ret = state & ((1 << bits) - 1);
|
|
102
|
+
state >>= bits;
|
|
103
|
+
offset -= bits;
|
|
104
|
+
return ret;
|
|
105
|
+
}
|
|
106
|
+
I val = state;
|
|
107
|
+
int out = offset;
|
|
108
|
+
while (out + 8 <= bits) {
|
|
109
|
+
val |= ((I(*(in++))) << out);
|
|
110
|
+
out += 8;
|
|
111
|
+
}
|
|
112
|
+
if (out < bits) {
|
|
113
|
+
unsigned char c = *(in++);
|
|
114
|
+
val |= (c & ((I(1) << (bits - out)) - 1)) << out;
|
|
115
|
+
state = c >> (bits - out);
|
|
116
|
+
offset = 8 - (bits - out);
|
|
117
|
+
} else {
|
|
118
|
+
state = 0;
|
|
119
|
+
offset = 0;
|
|
120
|
+
}
|
|
121
|
+
return val;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/** Return a value of type I with its `bits` lowest bits set (bits must be > 0). */
|
|
126
|
+
template<int BITS, typename I>
|
|
127
|
+
constexpr inline I Mask() { return ((I((I(-1)) << (std::numeric_limits<I>::digits - BITS))) >> (std::numeric_limits<I>::digits - BITS)); }
|
|
128
|
+
|
|
129
|
+
/** Compute the smallest power of two that is larger than val. */
|
|
130
|
+
template<typename I>
|
|
131
|
+
static inline int CountBits(I val, int max) {
|
|
132
|
+
#ifdef HAVE_CLZ
|
|
133
|
+
(void)max;
|
|
134
|
+
if (val == 0) return 0;
|
|
135
|
+
if (std::numeric_limits<unsigned>::digits >= std::numeric_limits<I>::digits) {
|
|
136
|
+
return std::numeric_limits<unsigned>::digits - __builtin_clz(val);
|
|
137
|
+
} else if (std::numeric_limits<unsigned long>::digits >= std::numeric_limits<I>::digits) {
|
|
138
|
+
return std::numeric_limits<unsigned long>::digits - __builtin_clzl(val);
|
|
139
|
+
} else {
|
|
140
|
+
return std::numeric_limits<unsigned long long>::digits - __builtin_clzll(val);
|
|
141
|
+
}
|
|
142
|
+
#elif _MSC_VER
|
|
143
|
+
(void)max;
|
|
144
|
+
unsigned long index;
|
|
145
|
+
unsigned char ret;
|
|
146
|
+
if (std::numeric_limits<I>::digits <= 32) {
|
|
147
|
+
ret = _BitScanReverse(&index, val);
|
|
148
|
+
} else {
|
|
149
|
+
ret = _BitScanReverse64(&index, val);
|
|
150
|
+
}
|
|
151
|
+
if (!ret) return 0;
|
|
152
|
+
return index;
|
|
153
|
+
#else
|
|
154
|
+
while (max && (val >> (max - 1) == 0)) --max;
|
|
155
|
+
return max;
|
|
156
|
+
#endif
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
template<typename I, int BITS>
|
|
160
|
+
class BitsInt {
|
|
161
|
+
private:
|
|
162
|
+
static_assert(std::is_integral<I>::value && std::is_unsigned<I>::value, "BitsInt requires an unsigned integer type");
|
|
163
|
+
static_assert(BITS > 0 && BITS <= std::numeric_limits<I>::digits, "BitsInt requires 1 <= Bits <= representation type size");
|
|
164
|
+
|
|
165
|
+
static constexpr I MASK = Mask<BITS, I>();
|
|
166
|
+
|
|
167
|
+
public:
|
|
168
|
+
|
|
169
|
+
typedef I Repr;
|
|
170
|
+
|
|
171
|
+
static constexpr int SIZE = BITS;
|
|
172
|
+
|
|
173
|
+
static void inline Swap(I& a, I& b) {
|
|
174
|
+
std::swap(a, b);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
static constexpr inline bool IsZero(I a) { return a == 0; }
|
|
178
|
+
static constexpr inline I Mask(I val) { return val & MASK; }
|
|
179
|
+
static constexpr inline I Shift(I val, int bits) { return ((val << bits) & MASK); }
|
|
180
|
+
static constexpr inline I UnsafeShift(I val, int bits) { return (val << bits); }
|
|
181
|
+
|
|
182
|
+
template<int Offset, int Count>
|
|
183
|
+
static constexpr inline int MidBits(I val) {
|
|
184
|
+
static_assert(Count > 0, "BITSInt::MidBits needs Count > 0");
|
|
185
|
+
static_assert(Count + Offset <= BITS, "BitsInt::MidBits overflow of Count+Offset");
|
|
186
|
+
return (val >> Offset) & ((I(1) << Count) - 1);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
template<int Count>
|
|
190
|
+
static constexpr inline int TopBits(I val) {
|
|
191
|
+
static_assert(Count > 0, "BitsInt::TopBits needs Count > 0");
|
|
192
|
+
static_assert(Count <= BITS, "BitsInt::TopBits needs Offset <= BITS");
|
|
193
|
+
return val >> (BITS - Count);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
static inline constexpr I CondXorWith(I val, bool cond, I v) {
|
|
197
|
+
return val ^ (-I(cond) & v);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
template<I MOD>
|
|
201
|
+
static inline constexpr I CondXorWith(I val, bool cond) {
|
|
202
|
+
return val ^ (-I(cond) & MOD);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
static inline int Bits(I val, int max) { return CountBits<I>(val, max); }
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/** Class which implements a stateless LFSR for generic moduli. */
|
|
209
|
+
template<typename F, uint32_t MOD>
|
|
210
|
+
struct LFSR {
|
|
211
|
+
typedef typename F::Repr I;
|
|
212
|
+
/** Shift a value `a` up once, treating it as an `N`-bit LFSR, with pattern `MOD`. */
|
|
213
|
+
static inline constexpr I Call(const I& a) {
|
|
214
|
+
return F::template CondXorWith<MOD>(F::Shift(a, 1), F::template TopBits<1>(a));
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/** Helper class for carryless multiplications. */
|
|
219
|
+
template<typename I, int N, typename L, typename F, int K> struct GFMulHelper;
|
|
220
|
+
template<typename I, int N, typename L, typename F> struct GFMulHelper<I, N, L, F, 0>
|
|
221
|
+
{
|
|
222
|
+
static inline constexpr I Run(const I& a, const I& b) { return I(0); }
|
|
223
|
+
};
|
|
224
|
+
template<typename I, int N, typename L, typename F, int K> struct GFMulHelper
|
|
225
|
+
{
|
|
226
|
+
static inline constexpr I Run(const I& a, const I& b) { return F::CondXorWith(GFMulHelper<I, N, L, F, K - 1>::Run(L::Call(a), b), F::template MidBits<N - K, 1>(b), a); }
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/** Compute the carry-less multiplication of a and b, with N bits, using L as LFSR type. */
|
|
230
|
+
template<typename I, int N, typename L, typename F> inline constexpr I GFMul(const I& a, const I& b) { return GFMulHelper<I, N, L, F, N>::Run(a, b); }
|
|
231
|
+
|
|
232
|
+
/** Compute the inverse of x using an extgcd algorithm. */
|
|
233
|
+
template<typename I, typename F, int BITS, uint32_t MOD>
|
|
234
|
+
inline I InvExtGCD(I x)
|
|
235
|
+
{
|
|
236
|
+
if (F::IsZero(x)) return x;
|
|
237
|
+
I t(0), newt(1);
|
|
238
|
+
I r(MOD), newr = x;
|
|
239
|
+
int rlen = BITS + 1, newrlen = F::Bits(newr, BITS);
|
|
240
|
+
while (newr) {
|
|
241
|
+
int q = rlen - newrlen;
|
|
242
|
+
r ^= F::Shift(newr, q);
|
|
243
|
+
t ^= F::UnsafeShift(newt, q);
|
|
244
|
+
rlen = F::Bits(r, rlen - 1);
|
|
245
|
+
if (r < newr) {
|
|
246
|
+
F::Swap(t, newt);
|
|
247
|
+
F::Swap(r, newr);
|
|
248
|
+
std::swap(rlen, newrlen);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return t;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Compute the inverse of x1 using an exponentiation ladder.
|
|
255
|
+
*
|
|
256
|
+
* The `MUL` argument is a multiplication function, `SQR` is a squaring function, and the `SQRi` arguments
|
|
257
|
+
* compute x**(2**i).
|
|
258
|
+
*/
|
|
259
|
+
template<typename I, typename F, int BITS, I (*MUL)(I, I), I (*SQR)(I), I (*SQR2)(I), I(*SQR4)(I), I(*SQR8)(I), I(*SQR16)(I)>
|
|
260
|
+
inline I InvLadder(I x1)
|
|
261
|
+
{
|
|
262
|
+
static constexpr int INV_EXP = BITS - 1;
|
|
263
|
+
I x2 = (INV_EXP >= 2) ? MUL(SQR(x1), x1) : I();
|
|
264
|
+
I x4 = (INV_EXP >= 4) ? MUL(SQR2(x2), x2) : I();
|
|
265
|
+
I x8 = (INV_EXP >= 8) ? MUL(SQR4(x4), x4) : I();
|
|
266
|
+
I x16 = (INV_EXP >= 16) ? MUL(SQR8(x8), x8) : I();
|
|
267
|
+
I x32 = (INV_EXP >= 32) ? MUL(SQR16(x16), x16) : I();
|
|
268
|
+
I r;
|
|
269
|
+
if (INV_EXP >= 32) {
|
|
270
|
+
r = x32;
|
|
271
|
+
} else if (INV_EXP >= 16) {
|
|
272
|
+
r = x16;
|
|
273
|
+
} else if (INV_EXP >= 8) {
|
|
274
|
+
r = x8;
|
|
275
|
+
} else if (INV_EXP >= 4) {
|
|
276
|
+
r = x4;
|
|
277
|
+
} else if (INV_EXP >= 2) {
|
|
278
|
+
r = x2;
|
|
279
|
+
} else {
|
|
280
|
+
r = x1;
|
|
281
|
+
}
|
|
282
|
+
if (INV_EXP >= 32 && (INV_EXP & 16)) r = MUL(SQR16(r), x16);
|
|
283
|
+
if (INV_EXP >= 16 && (INV_EXP & 8)) r = MUL(SQR8(r), x8);
|
|
284
|
+
if (INV_EXP >= 8 && (INV_EXP & 4)) r = MUL(SQR4(r), x4);
|
|
285
|
+
if (INV_EXP >= 4 && (INV_EXP & 2)) r = MUL(SQR2(r), x2);
|
|
286
|
+
if (INV_EXP >= 2 && (INV_EXP & 1)) r = MUL(SQR(r), x1);
|
|
287
|
+
return SQR(r);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
#endif
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#ifndef BITCOIN_INTERFACES_CHAIN_H
|
|
6
6
|
#define BITCOIN_INTERFACES_CHAIN_H
|
|
7
7
|
|
|
8
|
+
#include <node/chainstate.h>
|
|
8
9
|
#include <primitives/transaction.h> // For CTransactionRef
|
|
9
10
|
#include <util/settings.h> // For util::SettingsValue
|
|
10
11
|
|
|
@@ -18,16 +19,13 @@
|
|
|
18
19
|
|
|
19
20
|
class ArgsManager;
|
|
20
21
|
class CBlock;
|
|
21
|
-
class CFeeRate;
|
|
22
22
|
class CRPCCommand;
|
|
23
23
|
class CScheduler;
|
|
24
24
|
class Coin;
|
|
25
25
|
class uint256;
|
|
26
26
|
enum class MemPoolRemovalReason;
|
|
27
|
-
enum class RBFTransactionState;
|
|
28
27
|
struct bilingual_str;
|
|
29
28
|
struct CBlockLocator;
|
|
30
|
-
struct FeeCalculation;
|
|
31
29
|
namespace node {
|
|
32
30
|
struct NodeContext;
|
|
33
31
|
} // namespace node
|
|
@@ -96,6 +94,8 @@ class Chain
|
|
|
96
94
|
public:
|
|
97
95
|
virtual ~Chain() {}
|
|
98
96
|
|
|
97
|
+
virtual ChainstateManager& chainman() = 0;
|
|
98
|
+
|
|
99
99
|
//! Get current chain height, not including genesis block (returns 0 if
|
|
100
100
|
//! chain only contains genesis block, nullopt if chain does not contain
|
|
101
101
|
//! any blocks)
|
|
@@ -104,8 +104,7 @@ public:
|
|
|
104
104
|
//! Get block hash. Height must be valid or this function will abort.
|
|
105
105
|
virtual uint256 getBlockHash(int height) = 0;
|
|
106
106
|
|
|
107
|
-
//! Check that the block is available on disk
|
|
108
|
-
//! pruned), and contains transactions.
|
|
107
|
+
//! Check that the block is available on disk, and contains transactions.
|
|
109
108
|
virtual bool haveBlockOnDisk(int height) = 0;
|
|
110
109
|
|
|
111
110
|
//! Get locator for the current chain tip.
|
|
@@ -158,9 +157,6 @@ public:
|
|
|
158
157
|
//! the height range from min_height to max_height, inclusive.
|
|
159
158
|
virtual bool hasBlocks(const uint256& block_hash, int min_height = 0, std::optional<int> max_height = {}) = 0;
|
|
160
159
|
|
|
161
|
-
//! Check if transaction is RBF opt in.
|
|
162
|
-
virtual RBFTransactionState isRBFOptIn(const CTransaction& tx) = 0;
|
|
163
|
-
|
|
164
160
|
//! Check if transaction is in mempool.
|
|
165
161
|
virtual bool isInMempool(const uint256& txid) = 0;
|
|
166
162
|
|
|
@@ -171,7 +167,6 @@ public:
|
|
|
171
167
|
//! amount specified by max_tx_fee, and broadcast to all peers if relay is set to true.
|
|
172
168
|
//! Return false if the transaction could not be added due to the fee or for another reason.
|
|
173
169
|
virtual bool broadcastTransaction(const CTransactionRef& tx,
|
|
174
|
-
const CAmount& max_tx_fee,
|
|
175
170
|
bool relay,
|
|
176
171
|
std::string& err_string) = 0;
|
|
177
172
|
|
|
@@ -186,27 +181,6 @@ public:
|
|
|
186
181
|
//! Check if transaction will pass the mempool's chain limits.
|
|
187
182
|
virtual bool checkChainLimits(const CTransactionRef& tx) = 0;
|
|
188
183
|
|
|
189
|
-
//! Estimate smart fee.
|
|
190
|
-
virtual CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc = nullptr) = 0;
|
|
191
|
-
|
|
192
|
-
//! Fee estimator max target.
|
|
193
|
-
virtual unsigned int estimateMaxBlocks() = 0;
|
|
194
|
-
|
|
195
|
-
//! Mempool minimum fee.
|
|
196
|
-
virtual CFeeRate mempoolMinFee() = 0;
|
|
197
|
-
|
|
198
|
-
//! Relay current minimum fee (from -minrelaytxfee and -incrementalrelayfee settings).
|
|
199
|
-
virtual CFeeRate relayMinFee() = 0;
|
|
200
|
-
|
|
201
|
-
//! Relay incremental fee setting (-incrementalrelayfee), reflecting cost of relay.
|
|
202
|
-
virtual CFeeRate relayIncrementalFee() = 0;
|
|
203
|
-
|
|
204
|
-
//! Relay dust fee setting (-dustrelayfee), reflecting lowest rate it's economical to spend.
|
|
205
|
-
virtual CFeeRate relayDustFee() = 0;
|
|
206
|
-
|
|
207
|
-
//! Check if any block has been pruned.
|
|
208
|
-
virtual bool havePruned() = 0;
|
|
209
|
-
|
|
210
184
|
//! Check if the node is ready to broadcast transactions.
|
|
211
185
|
virtual bool isReadyToBroadcast() = 0;
|
|
212
186
|
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
#define BITCOIN_INTERFACES_INIT_H
|
|
7
7
|
|
|
8
8
|
#include <memory>
|
|
9
|
+
#include <interfaces/wallet.h>
|
|
10
|
+
|
|
11
|
+
using interfaces::WalletLoader;
|
|
9
12
|
|
|
10
13
|
namespace node {
|
|
11
14
|
struct NodeContext;
|
|
@@ -16,7 +19,7 @@ class Chain;
|
|
|
16
19
|
class Echo;
|
|
17
20
|
class Ipc;
|
|
18
21
|
class Node;
|
|
19
|
-
class WalletLoader;
|
|
22
|
+
//class WalletLoader;
|
|
20
23
|
|
|
21
24
|
//! Initial interface created when a process is first started, and used to give
|
|
22
25
|
//! and get access to other interfaces (Node, Chain, Wallet, etc).
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
#define BITCOIN_INTERFACES_NODE_H
|
|
7
7
|
|
|
8
8
|
#include <consensus/amount.h>
|
|
9
|
+
#include <node/chainstate.h>
|
|
9
10
|
#include <net.h> // For NodeId
|
|
10
11
|
#include <net_types.h> // For banmap_t
|
|
11
12
|
#include <netaddress.h> // For Network
|
|
12
13
|
#include <netbase.h> // For ConnectionDirection
|
|
13
14
|
#include <support/allocators/secure.h> // For SecureString
|
|
15
|
+
#include <util/ui_change_type.h>
|
|
14
16
|
#include <util/translation.h>
|
|
15
17
|
|
|
16
18
|
#include <functional>
|
|
@@ -22,7 +24,6 @@
|
|
|
22
24
|
#include <vector>
|
|
23
25
|
|
|
24
26
|
class BanMan;
|
|
25
|
-
class CFeeRate;
|
|
26
27
|
class CNodeStats;
|
|
27
28
|
class Coin;
|
|
28
29
|
class RPCTimerInterface;
|
|
@@ -70,6 +71,8 @@ class Node
|
|
|
70
71
|
public:
|
|
71
72
|
virtual ~Node() {}
|
|
72
73
|
|
|
74
|
+
virtual ChainstateManager& chainman() = 0;
|
|
75
|
+
|
|
73
76
|
//! Init logging.
|
|
74
77
|
virtual void initLogging() = 0;
|
|
75
78
|
|
|
@@ -170,9 +173,6 @@ public:
|
|
|
170
173
|
//! Get network active.
|
|
171
174
|
virtual bool getNetworkActive() = 0;
|
|
172
175
|
|
|
173
|
-
//! Get dust relay fee.
|
|
174
|
-
virtual CFeeRate getDustRelayFee() = 0;
|
|
175
|
-
|
|
176
176
|
//! Execute rpc command.
|
|
177
177
|
virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0;
|
|
178
178
|
|
|
@@ -189,7 +189,7 @@ public:
|
|
|
189
189
|
virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0;
|
|
190
190
|
|
|
191
191
|
//! Broadcast transaction.
|
|
192
|
-
virtual TransactionError broadcastTransaction(CTransactionRef tx,
|
|
192
|
+
virtual TransactionError broadcastTransaction(CTransactionRef tx, std::string& err_string) = 0;
|
|
193
193
|
|
|
194
194
|
//! Get wallet loader.
|
|
195
195
|
virtual WalletLoader& walletLoader() = 0;
|
|
@@ -227,7 +227,7 @@ public:
|
|
|
227
227
|
virtual std::unique_ptr<Handler> handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) = 0;
|
|
228
228
|
|
|
229
229
|
//! Register handler for notify alert messages.
|
|
230
|
-
using NotifyAlertChangedFn = std::function<void()>;
|
|
230
|
+
using NotifyAlertChangedFn = std::function<void(const uint256 &hash, ChangeType status)>;
|
|
231
231
|
virtual std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) = 0;
|
|
232
232
|
|
|
233
233
|
//! Register handler for ban list messages.
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
#include <utility>
|
|
25
25
|
#include <vector>
|
|
26
26
|
|
|
27
|
-
class CFeeRate;
|
|
28
27
|
class CKey;
|
|
29
28
|
enum class FeeReason;
|
|
30
29
|
enum class OutputType;
|
|
@@ -156,26 +155,6 @@ public:
|
|
|
156
155
|
//! Abandon transaction.
|
|
157
156
|
virtual bool abandonTransaction(const uint256& txid) = 0;
|
|
158
157
|
|
|
159
|
-
//! Return whether transaction can be bumped.
|
|
160
|
-
virtual bool transactionCanBeBumped(const uint256& txid) = 0;
|
|
161
|
-
|
|
162
|
-
//! Create bump transaction.
|
|
163
|
-
virtual bool createBumpTransaction(const uint256& txid,
|
|
164
|
-
const wallet::CCoinControl& coin_control,
|
|
165
|
-
std::vector<bilingual_str>& errors,
|
|
166
|
-
CAmount& old_fee,
|
|
167
|
-
CAmount& new_fee,
|
|
168
|
-
CMutableTransaction& mtx) = 0;
|
|
169
|
-
|
|
170
|
-
//! Sign bump transaction.
|
|
171
|
-
virtual bool signBumpTransaction(CMutableTransaction& mtx) = 0;
|
|
172
|
-
|
|
173
|
-
//! Commit bump transaction.
|
|
174
|
-
virtual bool commitBumpTransaction(const uint256& txid,
|
|
175
|
-
CMutableTransaction&& mtx,
|
|
176
|
-
std::vector<bilingual_str>& errors,
|
|
177
|
-
uint256& bumped_txid) = 0;
|
|
178
|
-
|
|
179
158
|
//! Get a transaction.
|
|
180
159
|
virtual CTransactionRef getTx(const uint256& txid) = 0;
|
|
181
160
|
|
|
@@ -237,7 +216,7 @@ public:
|
|
|
237
216
|
|
|
238
217
|
//! Return wallet transaction output information.
|
|
239
218
|
virtual std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) = 0;
|
|
240
|
-
|
|
219
|
+
/*
|
|
241
220
|
//! Get required fee.
|
|
242
221
|
virtual CAmount getRequiredFee(unsigned int tx_bytes) = 0;
|
|
243
222
|
|
|
@@ -249,7 +228,7 @@ public:
|
|
|
249
228
|
|
|
250
229
|
//! Get tx confirm target.
|
|
251
230
|
virtual unsigned int getConfirmTarget() = 0;
|
|
252
|
-
|
|
231
|
+
*/
|
|
253
232
|
// Return whether HD enabled.
|
|
254
233
|
virtual bool hdEnabled() = 0;
|
|
255
234
|
|
|
@@ -268,9 +247,6 @@ public:
|
|
|
268
247
|
// Get default address type.
|
|
269
248
|
virtual OutputType getDefaultAddressType() = 0;
|
|
270
249
|
|
|
271
|
-
//! Get max tx fee.
|
|
272
|
-
virtual CAmount getDefaultMaxTxFee() = 0;
|
|
273
|
-
|
|
274
250
|
// Remove wallet.
|
|
275
251
|
virtual void remove() = 0;
|
|
276
252
|
|
|
@@ -311,6 +287,9 @@ public:
|
|
|
311
287
|
|
|
312
288
|
//! Return pointer to internal wallet class, useful for testing.
|
|
313
289
|
virtual wallet::CWallet* wallet() { return nullptr; }
|
|
290
|
+
// peercoin
|
|
291
|
+
virtual void relockWalletAfterDuration(int nDuration) = 0;
|
|
292
|
+
virtual std::shared_ptr<wallet::CWallet> getWallet() = 0;
|
|
314
293
|
};
|
|
315
294
|
|
|
316
295
|
//! Wallet chain client that in addition to having chain client methods for
|
|
@@ -365,6 +344,7 @@ struct WalletAddress
|
|
|
365
344
|
struct WalletBalances
|
|
366
345
|
{
|
|
367
346
|
CAmount balance = 0;
|
|
347
|
+
CAmount stake = 0;
|
|
368
348
|
CAmount unconfirmed_balance = 0;
|
|
369
349
|
CAmount immature_balance = 0;
|
|
370
350
|
bool have_watch_only = false;
|
|
@@ -374,7 +354,7 @@ struct WalletBalances
|
|
|
374
354
|
|
|
375
355
|
bool balanceChanged(const WalletBalances& prev) const
|
|
376
356
|
{
|
|
377
|
-
return balance != prev.balance || unconfirmed_balance != prev.unconfirmed_balance ||
|
|
357
|
+
return balance != prev.balance || stake != prev.stake || unconfirmed_balance != prev.unconfirmed_balance ||
|
|
378
358
|
immature_balance != prev.immature_balance || watch_only_balance != prev.watch_only_balance ||
|
|
379
359
|
unconfirmed_watch_only_balance != prev.unconfirmed_watch_only_balance ||
|
|
380
360
|
immature_watch_only_balance != prev.immature_watch_only_balance;
|
|
@@ -395,6 +375,7 @@ struct WalletTx
|
|
|
395
375
|
int64_t time;
|
|
396
376
|
std::map<std::string, std::string> value_map;
|
|
397
377
|
bool is_coinbase;
|
|
378
|
+
bool is_coinstake;
|
|
398
379
|
};
|
|
399
380
|
|
|
400
381
|
//! Updated transaction status.
|
|
@@ -408,6 +389,7 @@ struct WalletTxStatus
|
|
|
408
389
|
bool is_trusted;
|
|
409
390
|
bool is_abandoned;
|
|
410
391
|
bool is_coinbase;
|
|
392
|
+
bool is_coinstake;
|
|
411
393
|
bool is_in_main_chain;
|
|
412
394
|
};
|
|
413
395
|
|
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
// Copyright (c) 2012-2023 The Peercoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
#include <kernel.h>
|
|
6
|
+
#include <chainparams.h>
|
|
7
|
+
#include <validation.h>
|
|
8
|
+
#include <streams.h>
|
|
9
|
+
#include <timedata.h>
|
|
10
|
+
#include <bignum.h>
|
|
11
|
+
#include <txdb.h>
|
|
12
|
+
#include <consensus/validation.h>
|
|
13
|
+
#include <validation.h>
|
|
14
|
+
#include <random.h>
|
|
15
|
+
#include <script/interpreter.h>
|
|
16
|
+
|
|
17
|
+
#include <index/txindex.h>
|
|
18
|
+
|
|
19
|
+
#include <boost/assign/list_of.hpp>
|
|
20
|
+
|
|
21
|
+
using namespace std;
|
|
22
|
+
|
|
23
|
+
// Protocol switch time of v0.3 kernel protocol
|
|
24
|
+
unsigned int nProtocolV03SwitchTime = 1363800000;
|
|
25
|
+
unsigned int nProtocolV03TestSwitchTime = 1359781000;
|
|
26
|
+
// Protocol switch time of v0.4 kernel protocol
|
|
27
|
+
unsigned int nProtocolV04SwitchTime = 1399300000;
|
|
28
|
+
unsigned int nProtocolV04TestSwitchTime = 1395700000;
|
|
29
|
+
// Protocol switch time of v0.5 kernel protocol
|
|
30
|
+
unsigned int nProtocolV05SwitchTime = 1461700000;
|
|
31
|
+
unsigned int nProtocolV05TestSwitchTime = 1447700000;
|
|
32
|
+
// Protocol switch time of v0.6 kernel protocol
|
|
33
|
+
// supermajority hardfork: actual fork will happen later than switch time
|
|
34
|
+
const unsigned int nProtocolV06SwitchTime = 1513050000; // Tue 12 Dec 03:40:00 UTC 2017
|
|
35
|
+
const unsigned int nProtocolV06TestSwitchTime = 1508198400; // Tue 17 Oct 00:00:00 UTC 2017
|
|
36
|
+
// Protocol switch time for 0.7 kernel protocol
|
|
37
|
+
const unsigned int nProtocolV07SwitchTime = 1552392000; // Tue 12 Mar 12:00:00 UTC 2019
|
|
38
|
+
const unsigned int nProtocolV07TestSwitchTime = 1541505600; // Tue 06 Nov 12:00:00 UTC 2018
|
|
39
|
+
// Switch time for new BIPs from bitcoin 0.16.x
|
|
40
|
+
const uint32_t nBTC16BIPsSwitchTime = 1569931200; // Tue 01 Oct 12:00:00 UTC 2019
|
|
41
|
+
const uint32_t nBTC16BIPsTestSwitchTime = 1554811200; // Tue 09 Apr 12:00:00 UTC 2019
|
|
42
|
+
// Protocol switch time for v0.9 kernel protocol
|
|
43
|
+
const unsigned int nProtocolV09SwitchTime = 1591617600; // Mon 8 Jun 12:00:00 UTC 2020
|
|
44
|
+
const unsigned int nProtocolV09TestSwitchTime = 1581940800; // Mon 17 Feb 12:00:00 UTC 2020
|
|
45
|
+
// Protocol switch time for v10 kernel protocol
|
|
46
|
+
const unsigned int nProtocolV10SwitchTime = 1635768000; // Mon 1 Nov 12:00:00 UTC 2021
|
|
47
|
+
const unsigned int nProtocolV10TestSwitchTime = 1625140800; // Thu 1 Jul 12:00:00 UTC 2021
|
|
48
|
+
// Protocol switch time for v12 kernel protocol
|
|
49
|
+
const unsigned int nProtocolV12SwitchTime = 1681732800; // Mon 17 Apr 12:00:00 UTC 2023
|
|
50
|
+
const unsigned int nProtocolV12TestSwitchTime = 1669636800; // Mon 28 Nov 12:00:00 UTC 2022
|
|
51
|
+
|
|
52
|
+
// Hard checkpoints of stake modifiers to ensure they are deterministic
|
|
53
|
+
static std::map<int, unsigned int> mapStakeModifierCheckpoints =
|
|
54
|
+
boost::assign::map_list_of
|
|
55
|
+
( 0, 0x0e00670bu )
|
|
56
|
+
( 19080, 0xad4e4d29u )
|
|
57
|
+
( 30583, 0xdc7bf136u )
|
|
58
|
+
( 99999, 0xf555cfd2u )
|
|
59
|
+
(219999, 0x91b7444du )
|
|
60
|
+
(336000, 0x6c3c8048u )
|
|
61
|
+
(371850, 0x9b850bdfu )
|
|
62
|
+
(407813, 0x46fe50b5u )
|
|
63
|
+
(443561, 0x114a6e38u )
|
|
64
|
+
(455470, 0x9b7af181u )
|
|
65
|
+
(479189, 0xe04fb8e0u )
|
|
66
|
+
(504051, 0x459f5a16u )
|
|
67
|
+
(589659, 0xbd02492au )
|
|
68
|
+
;
|
|
69
|
+
|
|
70
|
+
static std::map<int, unsigned int> mapStakeModifierTestnetCheckpoints =
|
|
71
|
+
boost::assign::map_list_of
|
|
72
|
+
( 0, 0x0e00670bu )
|
|
73
|
+
( 19080, 0x3711dc3au )
|
|
74
|
+
( 30583, 0xb480fadeu )
|
|
75
|
+
( 99999, 0x9a62eaecu )
|
|
76
|
+
(219999, 0xeafe96c3u )
|
|
77
|
+
(336000, 0x8330dc09u )
|
|
78
|
+
(372751, 0xafb94e2fu )
|
|
79
|
+
(382019, 0x7f5cf5ebu )
|
|
80
|
+
(408500, 0x68cadee2u )
|
|
81
|
+
(412691, 0x93138e67u )
|
|
82
|
+
(441299, 0x03e195cbu )
|
|
83
|
+
(442735, 0xe42d94feu )
|
|
84
|
+
;
|
|
85
|
+
|
|
86
|
+
// Whether the given coinstake is subject to new v0.3 protocol
|
|
87
|
+
bool IsProtocolV03(unsigned int nTimeCoinStake)
|
|
88
|
+
{
|
|
89
|
+
return (nTimeCoinStake >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV03TestSwitchTime : nProtocolV03SwitchTime));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Whether the given block is subject to new v0.4 protocol
|
|
93
|
+
bool IsProtocolV04(unsigned int nTimeBlock)
|
|
94
|
+
{
|
|
95
|
+
return (nTimeBlock >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV04TestSwitchTime : nProtocolV04SwitchTime));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Whether the given transaction is subject to new v0.5 protocol
|
|
99
|
+
bool IsProtocolV05(unsigned int nTimeTx)
|
|
100
|
+
{
|
|
101
|
+
return (nTimeTx >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV05TestSwitchTime : nProtocolV05SwitchTime));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Whether a given block is subject to new v0.6 protocol
|
|
105
|
+
// Test against previous block index! (always available)
|
|
106
|
+
bool IsProtocolV06(const CBlockIndex* pindexPrev)
|
|
107
|
+
{
|
|
108
|
+
if (pindexPrev->nTime < (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV06TestSwitchTime : nProtocolV06SwitchTime))
|
|
109
|
+
return false;
|
|
110
|
+
|
|
111
|
+
// if 900 of the last 1,000 blocks are version 2 or greater (90/100 if testnet):
|
|
112
|
+
// Soft-forking PoS can be dangerous if the super majority is too low
|
|
113
|
+
// The stake majority will decrease after the fork
|
|
114
|
+
// since only coindays of updated nodes will get destroyed.
|
|
115
|
+
if ((Params().NetworkIDString() == CBaseChainParams::MAIN && IsSuperMajority(2, pindexPrev, 900, 1000)) ||
|
|
116
|
+
(Params().NetworkIDString() != CBaseChainParams::MAIN && IsSuperMajority(2, pindexPrev, 90, 100)))
|
|
117
|
+
return true;
|
|
118
|
+
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Whether a given transaction is subject to new v0.7 protocol
|
|
123
|
+
bool IsProtocolV07(unsigned int nTimeTx)
|
|
124
|
+
{
|
|
125
|
+
bool fTestNet = Params().NetworkIDString() != CBaseChainParams::MAIN;
|
|
126
|
+
return (nTimeTx >= (fTestNet? nProtocolV07TestSwitchTime : nProtocolV07SwitchTime));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
bool IsBTC16BIPsEnabled(uint32_t nTimeTx)
|
|
130
|
+
{
|
|
131
|
+
bool fTestNet = Params().NetworkIDString() != CBaseChainParams::MAIN;
|
|
132
|
+
return (nTimeTx >= (fTestNet? nBTC16BIPsTestSwitchTime : nBTC16BIPsSwitchTime));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Whether a given timestamp is subject to new v0.9 protocol
|
|
136
|
+
bool IsProtocolV09(unsigned int nTime)
|
|
137
|
+
{
|
|
138
|
+
return (nTime >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV09TestSwitchTime : nProtocolV09SwitchTime));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Whether a given timestamp is subject to new v10 protocol
|
|
142
|
+
bool IsProtocolV10(unsigned int nTime)
|
|
143
|
+
{
|
|
144
|
+
return (nTime >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV10TestSwitchTime : nProtocolV10SwitchTime));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Whether a given timestamp is subject to new v10 protocol
|
|
148
|
+
bool IsProtocolV12(const CBlockIndex* pindexPrev)
|
|
149
|
+
{
|
|
150
|
+
if (pindexPrev->nTime < (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV12TestSwitchTime : nProtocolV12SwitchTime))
|
|
151
|
+
return false;
|
|
152
|
+
|
|
153
|
+
if ((Params().NetworkIDString() == CBaseChainParams::MAIN && IsSuperMajority(4, pindexPrev, 900, 1000)) ||
|
|
154
|
+
(Params().NetworkIDString() != CBaseChainParams::MAIN && IsSuperMajority(4, pindexPrev, 90, 100)))
|
|
155
|
+
return true;
|
|
156
|
+
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Get the last stake modifier and its generation time from a given block
|
|
161
|
+
static bool GetLastStakeModifier(const CBlockIndex* pindex, uint64_t& nStakeModifier, int64_t& nModifierTime)
|
|
162
|
+
{
|
|
163
|
+
if (!pindex)
|
|
164
|
+
return error("GetLastStakeModifier: null pindex");
|
|
165
|
+
while (pindex && pindex->pprev && !pindex->GeneratedStakeModifier())
|
|
166
|
+
pindex = pindex->pprev;
|
|
167
|
+
if (!pindex->GeneratedStakeModifier())
|
|
168
|
+
return error("GetLastStakeModifier: no generation at genesis block");
|
|
169
|
+
nStakeModifier = pindex->nStakeModifier;
|
|
170
|
+
nModifierTime = pindex->GetBlockTime();
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Get selection interval section (in seconds)
|
|
175
|
+
static int64_t GetStakeModifierSelectionIntervalSection(int nSection)
|
|
176
|
+
{
|
|
177
|
+
assert (nSection >= 0 && nSection < 64);
|
|
178
|
+
return (Params().GetConsensus().nModifierInterval * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1))));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Get stake modifier selection interval (in seconds)
|
|
182
|
+
static int64_t GetStakeModifierSelectionInterval()
|
|
183
|
+
{
|
|
184
|
+
int64_t nSelectionInterval = 0;
|
|
185
|
+
for (int nSection=0; nSection<64; nSection++)
|
|
186
|
+
nSelectionInterval += GetStakeModifierSelectionIntervalSection(nSection);
|
|
187
|
+
return nSelectionInterval;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// select a block from the candidate blocks in vSortedByTimestamp, excluding
|
|
191
|
+
// already selected blocks in vSelectedBlocks, and with timestamp up to
|
|
192
|
+
// nSelectionIntervalStop.
|
|
193
|
+
static bool SelectBlockFromCandidates(
|
|
194
|
+
vector<pair<int64_t, uint256> >& vSortedByTimestamp,
|
|
195
|
+
map<uint256, const CBlockIndex*>& mapSelectedBlocks,
|
|
196
|
+
int64_t nSelectionIntervalStop, uint64_t nStakeModifierPrev,
|
|
197
|
+
const CBlockIndex** pindexSelected,
|
|
198
|
+
CChainState& chainstate)
|
|
199
|
+
{
|
|
200
|
+
bool fSelected = false;
|
|
201
|
+
arith_uint256 hashBest = 0;
|
|
202
|
+
*pindexSelected = (const CBlockIndex*) 0;
|
|
203
|
+
for (const auto& item : vSortedByTimestamp)
|
|
204
|
+
{
|
|
205
|
+
/*
|
|
206
|
+
if (!chainstate.BlockIndex().count(item.second))
|
|
207
|
+
return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString());
|
|
208
|
+
const CBlockIndex* pindex = pindexSelected.BlockIndex()[item.second];
|
|
209
|
+
*/
|
|
210
|
+
const CBlockIndex* pindex = chainstate.m_blockman.LookupBlockIndex(item.second);
|
|
211
|
+
if (!pindex)
|
|
212
|
+
return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString());
|
|
213
|
+
|
|
214
|
+
if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop)
|
|
215
|
+
break;
|
|
216
|
+
if (mapSelectedBlocks.count(pindex->GetBlockHash()) > 0)
|
|
217
|
+
continue;
|
|
218
|
+
// compute the selection hash by hashing its proof-hash and the
|
|
219
|
+
// previous proof-of-stake modifier
|
|
220
|
+
uint256 hashProof = pindex->IsProofOfStake()? pindex->hashProofOfStake : pindex->GetBlockHash();
|
|
221
|
+
CDataStream ss(SER_GETHASH, 0);
|
|
222
|
+
ss << hashProof << nStakeModifierPrev;
|
|
223
|
+
arith_uint256 hashSelection = UintToArith256(Hash(ss));
|
|
224
|
+
// the selection hash is divided by 2**32 so that proof-of-stake block
|
|
225
|
+
// is always favored over proof-of-work block. this is to preserve
|
|
226
|
+
// the energy efficiency property
|
|
227
|
+
if (pindex->IsProofOfStake())
|
|
228
|
+
hashSelection >>= 32;
|
|
229
|
+
if (fSelected && hashSelection < hashBest)
|
|
230
|
+
{
|
|
231
|
+
hashBest = hashSelection;
|
|
232
|
+
*pindexSelected = (const CBlockIndex*) pindex;
|
|
233
|
+
}
|
|
234
|
+
else if (!fSelected)
|
|
235
|
+
{
|
|
236
|
+
fSelected = true;
|
|
237
|
+
hashBest = hashSelection;
|
|
238
|
+
*pindexSelected = (const CBlockIndex*) pindex;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printstakemodifier", false))
|
|
242
|
+
LogPrintf("SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString());
|
|
243
|
+
return fSelected;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Stake Modifier (hash modifier of proof-of-stake):
|
|
247
|
+
// The purpose of stake modifier is to prevent a txout (coin) owner from
|
|
248
|
+
// computing future proof-of-stake generated by this txout at the time
|
|
249
|
+
// of transaction confirmation. To meet kernel protocol, the txout
|
|
250
|
+
// must hash with a future stake modifier to generate the proof.
|
|
251
|
+
// Stake modifier consists of bits each of which is contributed from a
|
|
252
|
+
// selected block of a given block group in the past.
|
|
253
|
+
// The selection of a block is based on a hash of the block's proof-hash and
|
|
254
|
+
// the previous stake modifier.
|
|
255
|
+
// Stake modifier is recomputed at a fixed time interval instead of every
|
|
256
|
+
// block. This is to make it difficult for an attacker to gain control of
|
|
257
|
+
// additional bits in the stake modifier, even after generating a chain of
|
|
258
|
+
// blocks.
|
|
259
|
+
bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t &nStakeModifier, bool& fGeneratedStakeModifier, CChainState& chainstate)
|
|
260
|
+
{
|
|
261
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
262
|
+
const CBlockIndex* pindexPrev = pindexCurrent->pprev;
|
|
263
|
+
nStakeModifier = 0;
|
|
264
|
+
fGeneratedStakeModifier = false;
|
|
265
|
+
if (!pindexPrev)
|
|
266
|
+
{
|
|
267
|
+
fGeneratedStakeModifier = true;
|
|
268
|
+
return true; // genesis block's modifier is 0
|
|
269
|
+
}
|
|
270
|
+
// First find current stake modifier and its generation block time
|
|
271
|
+
// if it's not old enough, return the same stake modifier
|
|
272
|
+
int64_t nModifierTime = 0;
|
|
273
|
+
if (!GetLastStakeModifier(pindexPrev, nStakeModifier, nModifierTime))
|
|
274
|
+
return error("ComputeNextStakeModifier: unable to get last modifier");
|
|
275
|
+
if (gArgs.GetBoolArg("-debug", false))
|
|
276
|
+
LogPrintf("ComputeNextStakeModifier: prev modifier=0x%016x time=%s epoch=%u\n", nStakeModifier, FormatISO8601DateTime(nModifierTime), (unsigned int)nModifierTime);
|
|
277
|
+
if (nModifierTime / params.nModifierInterval >= pindexPrev->GetBlockTime() / params.nModifierInterval)
|
|
278
|
+
{
|
|
279
|
+
if (gArgs.GetBoolArg("-debug", false))
|
|
280
|
+
LogPrintf("ComputeNextStakeModifier: no new interval keep current modifier: pindexPrev nHeight=%d nTime=%u\n", pindexPrev->nHeight, (unsigned int)pindexPrev->GetBlockTime());
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
if (nModifierTime / params.nModifierInterval >= pindexCurrent->GetBlockTime() / params.nModifierInterval)
|
|
284
|
+
{
|
|
285
|
+
// v0.4+ requires current block timestamp also be in a different modifier interval
|
|
286
|
+
if (IsProtocolV04(pindexCurrent->nTime))
|
|
287
|
+
{
|
|
288
|
+
if (gArgs.GetBoolArg("-debug", false))
|
|
289
|
+
LogPrintf("ComputeNextStakeModifier: (v0.4+) no new interval keep current modifier: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime());
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
else
|
|
293
|
+
{
|
|
294
|
+
if (gArgs.GetBoolArg("-debug", false))
|
|
295
|
+
LogPrintf("ComputeNextStakeModifier: v0.3 modifier at block %s not meeting v0.4+ protocol: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->GetBlockHash().ToString(), pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime());
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Sort candidate blocks by timestamp
|
|
300
|
+
vector<pair<int64_t, uint256> > vSortedByTimestamp;
|
|
301
|
+
vSortedByTimestamp.reserve(64 * params.nModifierInterval / params.nStakeTargetSpacing);
|
|
302
|
+
int64_t nSelectionInterval = GetStakeModifierSelectionInterval();
|
|
303
|
+
int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / params.nModifierInterval) * params.nModifierInterval - nSelectionInterval;
|
|
304
|
+
const CBlockIndex* pindex = pindexPrev;
|
|
305
|
+
while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart)
|
|
306
|
+
{
|
|
307
|
+
vSortedByTimestamp.push_back(make_pair(pindex->GetBlockTime(), pindex->GetBlockHash()));
|
|
308
|
+
pindex = pindex->pprev;
|
|
309
|
+
}
|
|
310
|
+
int nHeightFirstCandidate = pindex ? (pindex->nHeight + 1) : 0;
|
|
311
|
+
|
|
312
|
+
// Shuffle before sort
|
|
313
|
+
for(int i = vSortedByTimestamp.size() - 1; i > 1; --i)
|
|
314
|
+
std::swap(vSortedByTimestamp[i], vSortedByTimestamp[GetRand(i)]);
|
|
315
|
+
|
|
316
|
+
sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end(), [] (const pair<int64_t, uint256> &a, const pair<int64_t, uint256> &b)
|
|
317
|
+
{
|
|
318
|
+
if (a.first != b.first)
|
|
319
|
+
return a.first < b.first;
|
|
320
|
+
// Timestamp equals - compare block hashes
|
|
321
|
+
const uint32_t *pa = a.second.GetDataPtr();
|
|
322
|
+
const uint32_t *pb = b.second.GetDataPtr();
|
|
323
|
+
int cnt = 256 / 32;
|
|
324
|
+
do {
|
|
325
|
+
--cnt;
|
|
326
|
+
if (pa[cnt] != pb[cnt])
|
|
327
|
+
return pa[cnt] < pb[cnt];
|
|
328
|
+
} while(cnt);
|
|
329
|
+
return false; // Elements are equal
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Select 64 blocks from candidate blocks to generate stake modifier
|
|
333
|
+
uint64_t nStakeModifierNew = 0;
|
|
334
|
+
int64_t nSelectionIntervalStop = nSelectionIntervalStart;
|
|
335
|
+
map<uint256, const CBlockIndex*> mapSelectedBlocks;
|
|
336
|
+
for (int nRound=0; nRound<min(64, (int)vSortedByTimestamp.size()); nRound++)
|
|
337
|
+
{
|
|
338
|
+
// add an interval section to the current selection round
|
|
339
|
+
nSelectionIntervalStop += GetStakeModifierSelectionIntervalSection(nRound);
|
|
340
|
+
// select a block from the candidates of current round
|
|
341
|
+
if (!SelectBlockFromCandidates(vSortedByTimestamp, mapSelectedBlocks, nSelectionIntervalStop, nStakeModifier, &pindex, chainstate))
|
|
342
|
+
return error("ComputeNextStakeModifier: unable to select block at round %d", nRound);
|
|
343
|
+
// write the entropy bit of the selected block
|
|
344
|
+
nStakeModifierNew |= (((uint64_t)pindex->GetStakeEntropyBit()) << nRound);
|
|
345
|
+
// add the selected block from candidates to selected list
|
|
346
|
+
mapSelectedBlocks.insert(make_pair(pindex->GetBlockHash(), pindex));
|
|
347
|
+
if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printstakemodifier", false))
|
|
348
|
+
LogPrintf("ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d\n",
|
|
349
|
+
nRound, FormatISO8601DateTime(nSelectionIntervalStop), pindex->nHeight, pindex->GetStakeEntropyBit());
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Print selection map for visualization of the selected blocks
|
|
353
|
+
if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printstakemodifier", false))
|
|
354
|
+
{
|
|
355
|
+
string strSelectionMap = "";
|
|
356
|
+
// '-' indicates proof-of-work blocks not selected
|
|
357
|
+
strSelectionMap.insert(0, pindexPrev->nHeight - nHeightFirstCandidate + 1, '-');
|
|
358
|
+
pindex = pindexPrev;
|
|
359
|
+
while (pindex && pindex->nHeight >= nHeightFirstCandidate)
|
|
360
|
+
{
|
|
361
|
+
// '=' indicates proof-of-stake blocks not selected
|
|
362
|
+
if (pindex->IsProofOfStake())
|
|
363
|
+
strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "=");
|
|
364
|
+
pindex = pindex->pprev;
|
|
365
|
+
}
|
|
366
|
+
for (const auto& item : mapSelectedBlocks)
|
|
367
|
+
{
|
|
368
|
+
// 'S' indicates selected proof-of-stake blocks
|
|
369
|
+
// 'W' indicates selected proof-of-work blocks
|
|
370
|
+
strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake()? "S" : "W");
|
|
371
|
+
}
|
|
372
|
+
LogPrintf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap);
|
|
373
|
+
}
|
|
374
|
+
if (gArgs.GetBoolArg("-debug", false))
|
|
375
|
+
LogPrintf("ComputeNextStakeModifier: new modifier=0x%016x time=%s\n", nStakeModifierNew, FormatISO8601DateTime(pindexPrev->GetBlockTime()));
|
|
376
|
+
|
|
377
|
+
nStakeModifier = nStakeModifierNew;
|
|
378
|
+
fGeneratedStakeModifier = true;
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// V0.5: Stake modifier used to hash for a stake kernel is chosen as the stake
|
|
383
|
+
// modifier that is (nStakeMinAge minus a selection interval) earlier than the
|
|
384
|
+
// stake, thus at least a selection interval later than the coin generating the
|
|
385
|
+
// kernel, as the generating coin is from at least nStakeMinAge ago.
|
|
386
|
+
static bool GetKernelStakeModifierV05(CBlockIndex* pindexPrev, unsigned int nTimeTx, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake)
|
|
387
|
+
{
|
|
388
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
389
|
+
const CBlockIndex* pindex = pindexPrev;
|
|
390
|
+
nStakeModifierHeight = pindex->nHeight;
|
|
391
|
+
nStakeModifierTime = pindex->GetBlockTime();
|
|
392
|
+
int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();
|
|
393
|
+
|
|
394
|
+
if (nStakeModifierTime + params.nStakeMinAge - nStakeModifierSelectionInterval <= (int64_t) nTimeTx)
|
|
395
|
+
{
|
|
396
|
+
// Best block is still more than
|
|
397
|
+
// (nStakeMinAge minus a selection interval) older than kernel timestamp
|
|
398
|
+
if (fPrintProofOfStake)
|
|
399
|
+
return error("GetKernelStakeModifier() : best block %s at height %d too old for stake",
|
|
400
|
+
pindex->GetBlockHash().ToString(), pindex->nHeight);
|
|
401
|
+
else
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
// loop to find the stake modifier earlier by
|
|
405
|
+
// (nStakeMinAge minus a selection interval)
|
|
406
|
+
while (nStakeModifierTime + params.nStakeMinAge - nStakeModifierSelectionInterval >(int64_t) nTimeTx)
|
|
407
|
+
{
|
|
408
|
+
if (!pindex->pprev)
|
|
409
|
+
{ // reached genesis block; should not happen
|
|
410
|
+
return error("GetKernelStakeModifier() : reached genesis block");
|
|
411
|
+
}
|
|
412
|
+
pindex = pindex->pprev;
|
|
413
|
+
if (pindex->GeneratedStakeModifier())
|
|
414
|
+
{
|
|
415
|
+
nStakeModifierHeight = pindex->nHeight;
|
|
416
|
+
nStakeModifierTime = pindex->GetBlockTime();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
nStakeModifier = pindex->nStakeModifier;
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// V0.3: Stake modifier used to hash for a stake kernel is chosen as the stake
|
|
424
|
+
// modifier about a selection interval later than the coin generating the kernel
|
|
425
|
+
static bool GetKernelStakeModifierV03(CBlockIndex* pindexPrev, uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake, CChainState& chainstate)
|
|
426
|
+
{
|
|
427
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
428
|
+
nStakeModifier = 0;
|
|
429
|
+
|
|
430
|
+
const CBlockIndex* pindexFrom = chainstate.m_blockman.LookupBlockIndex(hashBlockFrom);
|
|
431
|
+
if (!pindexFrom)
|
|
432
|
+
return error("GetKernelStakeModifier() : block not indexed");
|
|
433
|
+
|
|
434
|
+
nStakeModifierHeight = pindexFrom->nHeight;
|
|
435
|
+
nStakeModifierTime = pindexFrom->GetBlockTime();
|
|
436
|
+
int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
// we need to iterate index forward but we cannot depend on chainActive.Next()
|
|
440
|
+
// because there is no guarantee that we are checking blocks in active chain.
|
|
441
|
+
// So, we construct a temporary chain that we will iterate over.
|
|
442
|
+
// pindexFrom - this block contains coins that are used to generate PoS
|
|
443
|
+
// pindexPrev - this is a block that is previous to PoS block that we are checking, you can think of it as tip of our chain
|
|
444
|
+
std::vector<CBlockIndex*> tmpChain;
|
|
445
|
+
int32_t nDepth = pindexPrev->nHeight - (pindexFrom->nHeight-1); // -1 is used to also include pindexFrom
|
|
446
|
+
tmpChain.reserve(nDepth);
|
|
447
|
+
CBlockIndex* it = pindexPrev;
|
|
448
|
+
for (int i=1; i<=nDepth && !chainstate.m_chain.Contains(it); i++) {
|
|
449
|
+
tmpChain.push_back(it);
|
|
450
|
+
it = it->pprev;
|
|
451
|
+
}
|
|
452
|
+
std::reverse(tmpChain.begin(), tmpChain.end());
|
|
453
|
+
size_t n = 0;
|
|
454
|
+
|
|
455
|
+
const CBlockIndex* pindex = pindexFrom;
|
|
456
|
+
// loop to find the stake modifier later by a selection interval
|
|
457
|
+
while (nStakeModifierTime < pindexFrom->GetBlockTime() + nStakeModifierSelectionInterval)
|
|
458
|
+
{
|
|
459
|
+
const CBlockIndex* old_pindex = pindex;
|
|
460
|
+
pindex = (!tmpChain.empty() && pindex->nHeight >= tmpChain[0]->nHeight - 1)? tmpChain[n++] : chainstate.m_chain.Next(pindex);
|
|
461
|
+
if (n > tmpChain.size() || pindex == NULL) // check if tmpChain[n+1] exists
|
|
462
|
+
{ // reached best block; may happen if node is behind on block chain
|
|
463
|
+
if (fPrintProofOfStake || (old_pindex->GetBlockTime() + params.nStakeMinAge - nStakeModifierSelectionInterval > GetAdjustedTime()))
|
|
464
|
+
return error("GetKernelStakeModifier() : reached best block %s at height %d from block %s",
|
|
465
|
+
old_pindex->GetBlockHash().ToString(), old_pindex->nHeight, hashBlockFrom.ToString());
|
|
466
|
+
else
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
if (pindex->GeneratedStakeModifier())
|
|
470
|
+
{
|
|
471
|
+
nStakeModifierHeight = pindex->nHeight;
|
|
472
|
+
nStakeModifierTime = pindex->GetBlockTime();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
nStakeModifier = pindex->nStakeModifier;
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Get the stake modifier specified by the protocol to hash for a stake kernel
|
|
480
|
+
static bool GetKernelStakeModifier(CBlockIndex* pindexPrev, uint256 hashBlockFrom, unsigned int nTimeTx, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake, CChainState& chainstate)
|
|
481
|
+
{
|
|
482
|
+
if (IsProtocolV05(nTimeTx))
|
|
483
|
+
return GetKernelStakeModifierV05(pindexPrev, nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake);
|
|
484
|
+
else
|
|
485
|
+
return GetKernelStakeModifierV03(pindexPrev, hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake, chainstate);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// peercoin kernel protocol
|
|
489
|
+
// coinstake must meet hash target according to the protocol:
|
|
490
|
+
// kernel (input 0) must meet the formula
|
|
491
|
+
// hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDayWeight
|
|
492
|
+
// this ensures that the chance of getting a coinstake is proportional to the
|
|
493
|
+
// amount of coin age one owns.
|
|
494
|
+
// The reason this hash is chosen is the following:
|
|
495
|
+
// nStakeModifier:
|
|
496
|
+
// (v0.5) uses dynamic stake modifier around 21 days before the kernel,
|
|
497
|
+
// versus static stake modifier about 9 days after the staked
|
|
498
|
+
// coin (txPrev) used in v0.3
|
|
499
|
+
// (v0.3) scrambles computation to make it very difficult to precompute
|
|
500
|
+
// future proof-of-stake at the time of the coin's confirmation
|
|
501
|
+
// (v0.2) nBits (deprecated): encodes all past block timestamps
|
|
502
|
+
// txPrev.block.nTime: prevent nodes from guessing a good timestamp to
|
|
503
|
+
// generate transaction for future advantage
|
|
504
|
+
// txPrev.offset: offset of txPrev inside block, to reduce the chance of
|
|
505
|
+
// nodes generating coinstake at the same time
|
|
506
|
+
// txPrev.nTime: reduce the chance of nodes generating coinstake at the same
|
|
507
|
+
// time
|
|
508
|
+
// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes
|
|
509
|
+
// generating coinstake at the same time
|
|
510
|
+
// block/tx hash should not be used here as they can be generated in vast
|
|
511
|
+
// quantities so as to generate blocks faster, degrading the system back into
|
|
512
|
+
// a proof-of-work situation.
|
|
513
|
+
//
|
|
514
|
+
bool CheckStakeKernelHash(unsigned int nBits, CBlockIndex* pindexPrev, const CBlockHeader& blockFrom, unsigned int nTxPrevOffset, const CTransactionRef& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake, CChainState& chainstate)
|
|
515
|
+
{
|
|
516
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
517
|
+
unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();
|
|
518
|
+
|
|
519
|
+
if (nTimeTx < (txPrev->nTime? txPrev->nTime : nTimeBlockFrom)) // Transaction timestamp violation
|
|
520
|
+
return error("CheckStakeKernelHash() : nTime violation");
|
|
521
|
+
|
|
522
|
+
if (nTimeBlockFrom + params.nStakeMinAge > nTimeTx) // Min age requirement
|
|
523
|
+
return error("CheckStakeKernelHash() : min age violation");
|
|
524
|
+
|
|
525
|
+
CBigNum bnTargetPerCoinDay;
|
|
526
|
+
bnTargetPerCoinDay.SetCompact(nBits);
|
|
527
|
+
int64_t nValueIn = txPrev->vout[prevout.n].nValue;
|
|
528
|
+
// v0.3 protocol kernel hash weight starts from 0 at the 30-day min age
|
|
529
|
+
// this change increases active coins participating the hash and helps
|
|
530
|
+
// to secure the network when proof-of-stake difficulty is low
|
|
531
|
+
int64_t nTimeWeight = min((int64_t)nTimeTx - (txPrev->nTime? txPrev->nTime : nTimeBlockFrom), params.nStakeMaxAge) - (IsProtocolV03(nTimeTx)? params.nStakeMinAge : 0);
|
|
532
|
+
CBigNum bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60);
|
|
533
|
+
// Calculate hash
|
|
534
|
+
CDataStream ss(SER_GETHASH, 0);
|
|
535
|
+
uint64_t nStakeModifier = 0;
|
|
536
|
+
int nStakeModifierHeight = 0;
|
|
537
|
+
int64_t nStakeModifierTime = 0;
|
|
538
|
+
if (IsProtocolV03(nTimeTx)) // v0.3 protocol
|
|
539
|
+
{
|
|
540
|
+
if (!GetKernelStakeModifier(pindexPrev, blockFrom.GetHash(), nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake, chainstate))
|
|
541
|
+
return false;
|
|
542
|
+
ss << nStakeModifier;
|
|
543
|
+
}
|
|
544
|
+
else // v0.2 protocol
|
|
545
|
+
{
|
|
546
|
+
ss << nBits;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
ss << nTimeBlockFrom << nTxPrevOffset << (txPrev->nTime? txPrev->nTime : nTimeBlockFrom) << prevout.n << nTimeTx;
|
|
550
|
+
hashProofOfStake = Hash(ss);
|
|
551
|
+
if (fPrintProofOfStake)
|
|
552
|
+
{
|
|
553
|
+
if (IsProtocolV03(nTimeTx)) {
|
|
554
|
+
const CBlockIndex* pindexTmp = chainstate.m_blockman.LookupBlockIndex(blockFrom.GetHash());
|
|
555
|
+
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
|
|
556
|
+
nStakeModifier, nStakeModifierHeight,
|
|
557
|
+
FormatISO8601DateTime(nStakeModifierTime),
|
|
558
|
+
pindexTmp->nHeight,
|
|
559
|
+
FormatISO8601DateTime(blockFrom.GetBlockTime()));
|
|
560
|
+
}
|
|
561
|
+
LogPrintf("CheckStakeKernelHash() : check protocol=%s modifier=0x%016x nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
|
|
562
|
+
IsProtocolV05(nTimeTx)? "0.5" : (IsProtocolV03(nTimeTx)? "0.3" : "0.2"),
|
|
563
|
+
IsProtocolV03(nTimeTx)? nStakeModifier : (uint64_t) nBits,
|
|
564
|
+
nTimeBlockFrom, nTxPrevOffset, (txPrev->nTime? txPrev->nTime : nTimeBlockFrom), prevout.n, nTimeTx,
|
|
565
|
+
hashProofOfStake.ToString());
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Now check if proof-of-stake hash meets target protocol
|
|
569
|
+
if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay)
|
|
570
|
+
return false;
|
|
571
|
+
if (gArgs.GetBoolArg("-debug", false) && !fPrintProofOfStake)
|
|
572
|
+
{
|
|
573
|
+
if (IsProtocolV03(nTimeTx)) {
|
|
574
|
+
const CBlockIndex* pindexTmp = chainstate.m_blockman.LookupBlockIndex(blockFrom.GetHash());
|
|
575
|
+
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
|
|
576
|
+
nStakeModifier, nStakeModifierHeight,
|
|
577
|
+
FormatISO8601DateTime(nStakeModifierTime),
|
|
578
|
+
pindexTmp->nHeight,
|
|
579
|
+
FormatISO8601DateTime(blockFrom.GetBlockTime()));
|
|
580
|
+
}
|
|
581
|
+
LogPrintf("CheckStakeKernelHash() : pass protocol=%s modifier=0x%016x nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
|
|
582
|
+
IsProtocolV03(nTimeTx)? "0.3" : "0.2",
|
|
583
|
+
IsProtocolV03(nTimeTx)? nStakeModifier : (uint64_t) nBits,
|
|
584
|
+
nTimeBlockFrom, nTxPrevOffset, (txPrev->nTime? txPrev->nTime : nTimeBlockFrom), prevout.n, nTimeTx,
|
|
585
|
+
hashProofOfStake.ToString());
|
|
586
|
+
}
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Check kernel hash target and coinstake signature
|
|
591
|
+
bool CheckProofOfStake(BlockValidationState &state, CBlockIndex* pindexPrev, const CTransactionRef& tx, unsigned int nBits, uint256& hashProofOfStake, unsigned int nTimeTx, CChainState& chainstate)
|
|
592
|
+
{
|
|
593
|
+
if (!tx->IsCoinStake())
|
|
594
|
+
return error("CheckProofOfStake() : called on non-coinstake %s", tx->GetHash().ToString());
|
|
595
|
+
|
|
596
|
+
// Kernel (input 0) must match the stake hash target per coin age (nBits)
|
|
597
|
+
const CTxIn& txin = tx->vin[0];
|
|
598
|
+
|
|
599
|
+
// Transaction index is required to get to block header
|
|
600
|
+
if (!g_txindex)
|
|
601
|
+
return error("CheckProofOfStake() : transaction index not available");
|
|
602
|
+
|
|
603
|
+
// Get transaction index for the previous transaction
|
|
604
|
+
CDiskTxPos postx;
|
|
605
|
+
if (!g_txindex->FindTxPosition(txin.prevout.hash, postx))
|
|
606
|
+
return error("CheckProofOfStake() : tx index not found"); // tx index not found
|
|
607
|
+
|
|
608
|
+
// Read txPrev and header of its block
|
|
609
|
+
CBlockHeader header;
|
|
610
|
+
CTransactionRef txPrev;
|
|
611
|
+
auto it = g_txindex->cachedTxs.find(txin.prevout.hash);
|
|
612
|
+
if (it != g_txindex->cachedTxs.end()) {
|
|
613
|
+
header = it->second.first;
|
|
614
|
+
txPrev = it->second.second;
|
|
615
|
+
} else {
|
|
616
|
+
CAutoFile file(node::OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
|
617
|
+
try {
|
|
618
|
+
file >> header;
|
|
619
|
+
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
|
|
620
|
+
file >> txPrev;
|
|
621
|
+
} catch (std::exception &e) {
|
|
622
|
+
return error("%s() : deserialize or I/O error in CheckProofOfStake()", __PRETTY_FUNCTION__);
|
|
623
|
+
}
|
|
624
|
+
//g_txindex->cachedTxs[txin.prevout.hash] = std::pair(header,txPrev);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (txPrev->GetHash() != txin.prevout.hash)
|
|
628
|
+
return error("%s() : txid mismatch in CheckProofOfStake()", __PRETTY_FUNCTION__);
|
|
629
|
+
|
|
630
|
+
// Verify signature
|
|
631
|
+
{
|
|
632
|
+
int nIn = 0;
|
|
633
|
+
const CTxOut& prevOut = txPrev->vout[tx->vin[nIn].prevout.n];
|
|
634
|
+
TransactionSignatureChecker checker(&(*tx), nIn, prevOut.nValue, PrecomputedTransactionData(*tx), MissingDataBehavior(1));
|
|
635
|
+
|
|
636
|
+
if (!VerifyScript(tx->vin[nIn].scriptSig, prevOut.scriptPubKey, &(tx->vin[nIn].scriptWitness), SCRIPT_VERIFY_P2SH, checker, nullptr))
|
|
637
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "invalid-pos-script", strprintf("%s: VerifyScript failed on coinstake %s", __func__, tx->GetHash().ToString()));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (!CheckStakeKernelHash(nBits, pindexPrev, header, postx.nTxOffset + CBlockHeader::NORMAL_SERIALIZE_SIZE, txPrev, txin.prevout, nTimeTx, hashProofOfStake, gArgs.GetBoolArg("-debug", false), chainstate))
|
|
641
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "check-kernel-failed", strprintf("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx->GetHash().ToString(), hashProofOfStake.ToString())); // may occur during initial download or if behind on block chain sync
|
|
642
|
+
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Check whether the coinstake timestamp meets protocol
|
|
647
|
+
bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx)
|
|
648
|
+
{
|
|
649
|
+
if (IsProtocolV03(nTimeTx)) // v0.3 protocol
|
|
650
|
+
return (nTimeBlock == nTimeTx);
|
|
651
|
+
else // v0.2 protocol
|
|
652
|
+
return ((nTimeTx <= nTimeBlock) && (nTimeBlock <= nTimeTx + MAX_FUTURE_BLOCK_TIME_PREV9));
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Get stake modifier checksum
|
|
656
|
+
unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex)
|
|
657
|
+
{
|
|
658
|
+
assert (pindex->pprev || pindex->GetBlockHash() == Params().GetConsensus().hashGenesisBlock);
|
|
659
|
+
// Hash previous checksum with flags, hashProofOfStake and nStakeModifier
|
|
660
|
+
CDataStream ss(SER_GETHASH, 0);
|
|
661
|
+
if (pindex->pprev)
|
|
662
|
+
ss << pindex->pprev->nStakeModifierChecksum;
|
|
663
|
+
ss << pindex->nFlags << pindex->hashProofOfStake << pindex->nStakeModifier;
|
|
664
|
+
arith_uint256 hashChecksum = UintToArith256(Hash(ss));
|
|
665
|
+
hashChecksum >>= (256 - 32);
|
|
666
|
+
return hashChecksum.GetLow64();
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Check stake modifier hard checkpoints
|
|
670
|
+
bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum)
|
|
671
|
+
{
|
|
672
|
+
bool fTestNet = Params().NetworkIDString() == CBaseChainParams::TESTNET;
|
|
673
|
+
if (fTestNet && mapStakeModifierTestnetCheckpoints.count(nHeight))
|
|
674
|
+
return nStakeModifierChecksum == mapStakeModifierTestnetCheckpoints[nHeight];
|
|
675
|
+
|
|
676
|
+
if (!fTestNet && mapStakeModifierCheckpoints.count(nHeight))
|
|
677
|
+
return nStakeModifierChecksum == mapStakeModifierCheckpoints[nHeight];
|
|
678
|
+
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck)
|
|
683
|
+
{
|
|
684
|
+
return (HowSuperMajority(minVersion, pstart, nRequired, nToCheck) >= nRequired);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
unsigned int HowSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck)
|
|
688
|
+
{
|
|
689
|
+
unsigned int nFound = 0;
|
|
690
|
+
for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; pstart = pstart->pprev )
|
|
691
|
+
{
|
|
692
|
+
if (!pstart->IsProofOfStake())
|
|
693
|
+
continue;
|
|
694
|
+
|
|
695
|
+
if (pstart->nVersion >= minVersion)
|
|
696
|
+
++nFound;
|
|
697
|
+
|
|
698
|
+
i++;
|
|
699
|
+
}
|
|
700
|
+
return nFound;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// peercoin: entropy bit for stake modifier if chosen by modifier
|
|
704
|
+
unsigned int GetStakeEntropyBit(const CBlock& block)
|
|
705
|
+
{
|
|
706
|
+
unsigned int nEntropyBit = 0;
|
|
707
|
+
if (IsProtocolV04(block.nTime))
|
|
708
|
+
{
|
|
709
|
+
nEntropyBit = UintToArith256(block.GetHash()).GetLow64() & 1llu;// last bit of block hash
|
|
710
|
+
if (gArgs.GetBoolArg("-printstakemodifier", false))
|
|
711
|
+
LogPrintf("GetStakeEntropyBit(v0.4+): nTime=%u hashBlock=%s entropybit=%d\n", block.nTime, block.GetHash().ToString(), nEntropyBit);
|
|
712
|
+
}
|
|
713
|
+
else
|
|
714
|
+
{
|
|
715
|
+
// old protocol for entropy bit pre v0.4
|
|
716
|
+
uint160 hashSig = Hash160(block.vchBlockSig);
|
|
717
|
+
if (gArgs.GetBoolArg("-printstakemodifier", false))
|
|
718
|
+
LogPrintf("GetStakeEntropyBit(v0.3): nTime=%u hashSig=%s", block.nTime, hashSig.ToString());
|
|
719
|
+
nEntropyBit = hashSig.GetDataPtr()[4] >> 31; // take the first bit of the hash
|
|
720
|
+
if (gArgs.GetBoolArg("-printstakemodifier", false))
|
|
721
|
+
LogPrintf(" entropybit=%d\n", nEntropyBit);
|
|
722
|
+
}
|
|
723
|
+
return nEntropyBit;
|
|
724
|
+
}
|
|
725
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Copyright (c) 2012-2023 The Peercoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
#ifndef PEERCOIN_KERNEL_H
|
|
5
|
+
#define PEERCOIN_KERNEL_H
|
|
6
|
+
|
|
7
|
+
#include <primitives/transaction.h> // CTransaction(Ref)
|
|
8
|
+
|
|
9
|
+
class CBlockIndex;
|
|
10
|
+
class BlockValidationState;
|
|
11
|
+
class CBlockHeader;
|
|
12
|
+
class CBlock;
|
|
13
|
+
class CChainState;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// MODIFIER_INTERVAL_RATIO:
|
|
17
|
+
// ratio of group interval length between the last group and the first group
|
|
18
|
+
static const int MODIFIER_INTERVAL_RATIO = 3;
|
|
19
|
+
|
|
20
|
+
// Protocol switch time of v0.3 kernel protocol
|
|
21
|
+
extern unsigned int nProtocolV03SwitchTime;
|
|
22
|
+
extern unsigned int nProtocolV03TestSwitchTime;
|
|
23
|
+
|
|
24
|
+
// Whether a given coinstake is subject to new v0.3 protocol
|
|
25
|
+
bool IsProtocolV03(unsigned int nTimeCoinStake);
|
|
26
|
+
// Whether a given block is subject to new v0.4 protocol
|
|
27
|
+
bool IsProtocolV04(unsigned int nTimeBlock);
|
|
28
|
+
// Whether a given transaction is subject to new v0.5 protocol
|
|
29
|
+
bool IsProtocolV05(unsigned int nTimeTx);
|
|
30
|
+
// Whether a given block is subject to new v0.6 protocol
|
|
31
|
+
// Test against previous block index! (always available)
|
|
32
|
+
bool IsProtocolV06(const CBlockIndex *pindexPrev);
|
|
33
|
+
// Whether a given transaction is subject to new v0.7 protocol
|
|
34
|
+
bool IsProtocolV07(unsigned int nTimeTx);
|
|
35
|
+
// Whether a given block is subject to new BIPs from bitcoin 0.16.x
|
|
36
|
+
bool IsBTC16BIPsEnabled(uint32_t nTimeTx);
|
|
37
|
+
// Whether a given timestamp is subject to new v0.9 protocol
|
|
38
|
+
bool IsProtocolV09(unsigned int nTimeTx);
|
|
39
|
+
// Whether a given timestamp is subject to new v10 protocol
|
|
40
|
+
bool IsProtocolV10(unsigned int nTimeTx);
|
|
41
|
+
// Whether a given block is subject to new v12 protocol
|
|
42
|
+
bool IsProtocolV12(const CBlockIndex* pindexPrev);
|
|
43
|
+
|
|
44
|
+
// Compute the hash modifier for proof-of-stake
|
|
45
|
+
bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier, CChainState& chainstate);
|
|
46
|
+
|
|
47
|
+
// Check whether stake kernel meets hash target
|
|
48
|
+
// Sets hashProofOfStake on success return
|
|
49
|
+
bool CheckStakeKernelHash(unsigned int nBits, CBlockIndex* pindexPrev, const CBlockHeader& blockFrom, unsigned int nTxPrevOffset, const CTransactionRef& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake, CChainState& chainstate);
|
|
50
|
+
|
|
51
|
+
// Check kernel hash target and coinstake signature
|
|
52
|
+
// Sets hashProofOfStake on success return
|
|
53
|
+
bool CheckProofOfStake(BlockValidationState &state, CBlockIndex* pindexPrev, const CTransactionRef &tx, unsigned int nBits, uint256& hashProofOfStake, unsigned int nTimeTx, CChainState& chainstate);
|
|
54
|
+
|
|
55
|
+
// Check whether the coinstake timestamp meets protocol
|
|
56
|
+
bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx);
|
|
57
|
+
|
|
58
|
+
// Get stake modifier checksum
|
|
59
|
+
unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex);
|
|
60
|
+
|
|
61
|
+
// Check stake modifier hard checkpoints
|
|
62
|
+
bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum);
|
|
63
|
+
|
|
64
|
+
// peercoin: block version supermajority calculation
|
|
65
|
+
bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck);
|
|
66
|
+
unsigned int HowSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck);
|
|
67
|
+
|
|
68
|
+
// peercoin: entropy bit for stake modifier if chosen by modifier
|
|
69
|
+
unsigned int GetStakeEntropyBit(const CBlock& block);
|
|
70
|
+
|
|
71
|
+
#endif // PEERCOIN_KERNEL_H
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#include <kernelrecord.h>
|
|
2
|
+
#include <key_io.h>
|
|
3
|
+
#include <wallet/wallet.h>
|
|
4
|
+
#include <base58.h>
|
|
5
|
+
#include <chainparams.h>
|
|
6
|
+
#include <timedata.h>
|
|
7
|
+
#include <interfaces/wallet.h>
|
|
8
|
+
#include <math.h>
|
|
9
|
+
using namespace std;
|
|
10
|
+
|
|
11
|
+
bool KernelRecord::showTransaction(bool isCoinbase, int depth)
|
|
12
|
+
{
|
|
13
|
+
if (isCoinbase) {
|
|
14
|
+
if (depth < 2)
|
|
15
|
+
return false;
|
|
16
|
+
} else {
|
|
17
|
+
if (depth == 0)
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* Decompose CWallet transaction to model kernel records.
|
|
26
|
+
*/
|
|
27
|
+
vector<KernelRecord> KernelRecord::decomposeOutput(interfaces::Wallet& wallet, const interfaces::WalletTx &wtx)
|
|
28
|
+
{
|
|
29
|
+
vector<KernelRecord> parts;
|
|
30
|
+
int64_t nTime = (wtx.tx->nTime ? wtx.tx->nTime : wtx.time);
|
|
31
|
+
uint256 hash = wtx.tx->GetHash();
|
|
32
|
+
std::map<std::string, std::string> mapValue = wtx.value_map;
|
|
33
|
+
|
|
34
|
+
int numBlocks;
|
|
35
|
+
interfaces::WalletTxStatus status;
|
|
36
|
+
interfaces::WalletOrderForm orderForm;
|
|
37
|
+
bool inMempool;
|
|
38
|
+
wallet.getWalletTxDetails(hash, status, orderForm, inMempool, numBlocks);
|
|
39
|
+
|
|
40
|
+
if (showTransaction(wtx.is_coinbase, status.depth_in_main_chain)) {
|
|
41
|
+
for (size_t nOut = 0; nOut < wtx.tx->vout.size(); nOut++) {
|
|
42
|
+
CTxOut txOut = wtx.tx->vout[nOut];
|
|
43
|
+
if (wallet.txoutIsMine(txOut)) {
|
|
44
|
+
CTxDestination address;
|
|
45
|
+
std::string addrStr;
|
|
46
|
+
|
|
47
|
+
if (ExtractDestination(txOut.scriptPubKey, address)) {
|
|
48
|
+
// Sent to Bitcoin Address
|
|
49
|
+
addrStr = EncodeDestination(address);
|
|
50
|
+
} else {
|
|
51
|
+
// Sent to IP, or other non-address transaction like OP_EVAL
|
|
52
|
+
addrStr = mapValue["to"];
|
|
53
|
+
}
|
|
54
|
+
std::vector<interfaces::WalletTxOut> coins = wallet.getCoins({COutPoint(hash, nOut)});
|
|
55
|
+
bool isSpent = coins.size() >= 1 ? coins[0].is_spent : true;
|
|
56
|
+
parts.push_back(KernelRecord(hash, nTime, addrStr, txOut.nValue, nOut, isSpent));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return parts;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
std::string KernelRecord::getTxID()
|
|
65
|
+
{
|
|
66
|
+
return hash.ToString() + strprintf("-%03d", idx);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
int64_t KernelRecord::getAge() const
|
|
70
|
+
{
|
|
71
|
+
return (GetAdjustedTime() - nTime) / 86400;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
int64_t KernelRecord::getCoinAge() const
|
|
75
|
+
{
|
|
76
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
77
|
+
int nDayWeight = (min((GetAdjustedTime() - nTime), params.nStakeMaxAge) - params.nStakeMinAge) / 86400;
|
|
78
|
+
return max(nValue * nDayWeight / COIN, (int64_t) 0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
double KernelRecord::getProbToMintStake(double difficulty, int timeOffset) const
|
|
82
|
+
{
|
|
83
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
84
|
+
double maxTarget = pow(static_cast<double>(2), 224);
|
|
85
|
+
double target = maxTarget / difficulty;
|
|
86
|
+
int dayWeight = (min((GetAdjustedTime() - nTime) + timeOffset, params.nStakeMaxAge) - params.nStakeMinAge) / 86400;
|
|
87
|
+
uint64_t coinAge = max(nValue * dayWeight / COIN, (int64_t)0);
|
|
88
|
+
return target * coinAge / pow(static_cast<double>(2), 256);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
double KernelRecord::getProbToMintWithinNMinutes(double difficulty, int minutes)
|
|
92
|
+
{
|
|
93
|
+
if(difficulty != prevDifficulty || minutes != prevMinutes)
|
|
94
|
+
{
|
|
95
|
+
double prob = 1;
|
|
96
|
+
double p;
|
|
97
|
+
int d = minutes / (60 * 24); // Number of full days
|
|
98
|
+
int m = minutes % (60 * 24); // Number of minutes in the last day
|
|
99
|
+
int i, timeOffset;
|
|
100
|
+
|
|
101
|
+
// Probabilities for the first d days
|
|
102
|
+
for(i = 0; i < d; i++)
|
|
103
|
+
{
|
|
104
|
+
timeOffset = i * 86400;
|
|
105
|
+
p = pow(1 - getProbToMintStake(difficulty, timeOffset), 86400);
|
|
106
|
+
prob *= p;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Probability for the m minutes of the last day
|
|
110
|
+
timeOffset = d * 86400;
|
|
111
|
+
p = pow(1 - getProbToMintStake(difficulty, timeOffset), 60 * m);
|
|
112
|
+
prob *= p;
|
|
113
|
+
|
|
114
|
+
prob = 1 - prob;
|
|
115
|
+
prevProbability = prob;
|
|
116
|
+
prevDifficulty = difficulty;
|
|
117
|
+
prevMinutes = minutes;
|
|
118
|
+
}
|
|
119
|
+
return prevProbability;
|
|
120
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Copyright (c) 2012-2023 The Peercoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
#ifndef PEERCOIN_KERNELRECORD_H
|
|
5
|
+
#define PEERCOIN_KERNELRECORD_H
|
|
6
|
+
|
|
7
|
+
#include <uint256.h>
|
|
8
|
+
#include <interfaces/wallet.h>
|
|
9
|
+
|
|
10
|
+
namespace wallet {
|
|
11
|
+
class CWallet;
|
|
12
|
+
} // namespace wallet
|
|
13
|
+
//class CWallet;
|
|
14
|
+
using wallet::CWallet;
|
|
15
|
+
class CWalletTx;
|
|
16
|
+
|
|
17
|
+
class KernelRecord
|
|
18
|
+
{
|
|
19
|
+
public:
|
|
20
|
+
KernelRecord():
|
|
21
|
+
hash(), nTime(0), address(""), nValue(0), idx(0), spent(false), prevMinutes(0), prevDifficulty(0), prevProbability(0)
|
|
22
|
+
{
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
KernelRecord(uint256 hash, int64_t nTime):
|
|
26
|
+
hash(hash), nTime(nTime), address(""), nValue(0), idx(0), spent(false), prevMinutes(0), prevDifficulty(0), prevProbability(0)
|
|
27
|
+
{
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
KernelRecord(uint256 hash, int64_t nTime,
|
|
31
|
+
const std::string &address,
|
|
32
|
+
int64_t nValue, int idx, bool spent):
|
|
33
|
+
hash(hash), nTime(nTime), address(address), nValue(nValue),
|
|
34
|
+
idx(idx), spent(spent), prevMinutes(0), prevDifficulty(0), prevProbability(0)
|
|
35
|
+
{
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static bool showTransaction(bool isCoinbase, int depth);
|
|
39
|
+
static std::vector<KernelRecord> decomposeOutput(interfaces::Wallet &wallet, const interfaces::WalletTx &wtx);
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
uint256 hash;
|
|
43
|
+
int64_t nTime;
|
|
44
|
+
std::string address;
|
|
45
|
+
int64_t nValue;
|
|
46
|
+
int idx;
|
|
47
|
+
bool spent;
|
|
48
|
+
|
|
49
|
+
std::string getTxID();
|
|
50
|
+
int64_t getAge() const;
|
|
51
|
+
int64_t getCoinAge() const;
|
|
52
|
+
double getProbToMintStake(double difficulty, int timeOffset = 0) const;
|
|
53
|
+
double getProbToMintWithinNMinutes(double difficulty, int minutes);
|
|
54
|
+
protected:
|
|
55
|
+
int prevMinutes;
|
|
56
|
+
double prevDifficulty;
|
|
57
|
+
double prevProbability;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
#endif // PEERCOIN_KERNELRECORD_H
|
|
@@ -243,7 +243,7 @@ bool CKey::VerifyPubKey(const CPubKey& pubkey) const {
|
|
|
243
243
|
return false;
|
|
244
244
|
}
|
|
245
245
|
unsigned char rnd[8];
|
|
246
|
-
std::string str = "
|
|
246
|
+
std::string str = "Peercoin key verification\n";
|
|
247
247
|
GetRandBytes(rnd, sizeof(rnd));
|
|
248
248
|
uint256 hash;
|
|
249
249
|
CHash256().Write(MakeUCharSpan(str)).Write(rnd).Finalize(hash);
|
|
@@ -118,6 +118,10 @@ std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mute
|
|
|
118
118
|
static bool vfLimited[NET_MAX] GUARDED_BY(g_maplocalhost_mutex) = {};
|
|
119
119
|
std::string strSubVersion;
|
|
120
120
|
|
|
121
|
+
// peercoin: temperature to measure how many PoS headers have been sent by this client
|
|
122
|
+
std::map<CNetAddr, int32_t> mapPoSTemperature;
|
|
123
|
+
std::set<std::pair<COutPoint, unsigned int>> setStakeSeen;
|
|
124
|
+
|
|
121
125
|
void CConnman::AddAddrFetch(const std::string& strDest)
|
|
122
126
|
{
|
|
123
127
|
LOCK(m_addr_fetches_mutex);
|
|
@@ -3031,6 +3035,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> s
|
|
|
3031
3035
|
nLocalServices(nLocalServicesIn)
|
|
3032
3036
|
{
|
|
3033
3037
|
if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);
|
|
3038
|
+
lastAcceptedHeader = uint256();
|
|
3034
3039
|
if (conn_type_in != ConnectionType::BLOCK_RELAY) {
|
|
3035
3040
|
m_tx_relay = std::make_unique<TxRelay>();
|
|
3036
3041
|
}
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
#include <net_permissions.h>
|
|
18
18
|
#include <netaddress.h>
|
|
19
19
|
#include <netbase.h>
|
|
20
|
-
#include <policy/feerate.h>
|
|
21
20
|
#include <protocol.h>
|
|
22
21
|
#include <random.h>
|
|
23
22
|
#include <span.h>
|
|
@@ -88,6 +87,10 @@ static constexpr bool DEFAULT_FIXEDSEEDS{true};
|
|
|
88
87
|
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
|
|
89
88
|
static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
|
|
90
89
|
|
|
90
|
+
/** peercoin: Number of consecutive PoS headers are allowed from a single peer. Used to prevent out of memory attack. */
|
|
91
|
+
static const int32_t MAX_CONSECUTIVE_POS_HEADERS = 1000;
|
|
92
|
+
|
|
93
|
+
// const unsigned int POW_HEADER_COOLING = 70; - defined in protocol.cpp, so that it is visible to other files
|
|
91
94
|
typedef int64_t NodeId;
|
|
92
95
|
|
|
93
96
|
struct AddedNodeInfo
|
|
@@ -243,6 +246,8 @@ struct LocalServiceInfo {
|
|
|
243
246
|
|
|
244
247
|
extern Mutex g_maplocalhost_mutex;
|
|
245
248
|
extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mutex);
|
|
249
|
+
extern std::map<CNetAddr, int32_t> mapPoSTemperature;
|
|
250
|
+
extern std::set<std::pair<COutPoint, unsigned int>> setStakeSeen;
|
|
246
251
|
|
|
247
252
|
extern const std::string NET_MESSAGE_COMMAND_OTHER;
|
|
248
253
|
typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes
|
|
@@ -598,6 +603,8 @@ public:
|
|
|
598
603
|
/** Lowest measured round-trip time. Used as an inbound peer eviction
|
|
599
604
|
* criterium in CConnman::AttemptToEvictConnection. */
|
|
600
605
|
std::atomic<std::chrono::microseconds> m_min_ping_time{std::chrono::microseconds::max()};
|
|
606
|
+
// peercoin: used to detect branch switches
|
|
607
|
+
uint256 lastAcceptedHeader;
|
|
601
608
|
|
|
602
609
|
CNode(NodeId id, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> sock, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion);
|
|
603
610
|
CNode(const CNode&) = delete;
|
|
@@ -959,6 +966,14 @@ public:
|
|
|
959
966
|
/** Return true if we should disconnect the peer for failing an inactivity check. */
|
|
960
967
|
bool ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const;
|
|
961
968
|
|
|
969
|
+
/**
|
|
970
|
+
* This is signaled when network activity should cease.
|
|
971
|
+
* A pointer to it is saved in `m_i2p_sam_session`, so make sure that
|
|
972
|
+
* the lifetime of `interruptNet` is not shorter than
|
|
973
|
+
* the lifetime of `m_i2p_sam_session`.
|
|
974
|
+
*/
|
|
975
|
+
CThreadInterrupt interruptNet;
|
|
976
|
+
|
|
962
977
|
private:
|
|
963
978
|
struct ListenSocket {
|
|
964
979
|
public:
|
|
@@ -1203,14 +1218,6 @@ private:
|
|
|
1203
1218
|
Mutex mutexMsgProc;
|
|
1204
1219
|
std::atomic<bool> flagInterruptMsgProc{false};
|
|
1205
1220
|
|
|
1206
|
-
/**
|
|
1207
|
-
* This is signaled when network activity should cease.
|
|
1208
|
-
* A pointer to it is saved in `m_i2p_sam_session`, so make sure that
|
|
1209
|
-
* the lifetime of `interruptNet` is not shorter than
|
|
1210
|
-
* the lifetime of `m_i2p_sam_session`.
|
|
1211
|
-
*/
|
|
1212
|
-
CThreadInterrupt interruptNet;
|
|
1213
|
-
|
|
1214
1221
|
/**
|
|
1215
1222
|
* I2P SAM session.
|
|
1216
1223
|
* Used to accept incoming and make outgoing I2P connections.
|
|
@@ -9,17 +9,16 @@
|
|
|
9
9
|
#include <banman.h>
|
|
10
10
|
#include <blockencodings.h>
|
|
11
11
|
#include <blockfilter.h>
|
|
12
|
+
#include <chain.h>
|
|
12
13
|
#include <chainparams.h>
|
|
13
14
|
#include <consensus/amount.h>
|
|
14
15
|
#include <consensus/validation.h>
|
|
15
|
-
#include <deploymentstatus.h>
|
|
16
16
|
#include <hash.h>
|
|
17
17
|
#include <index/blockfilterindex.h>
|
|
18
18
|
#include <merkleblock.h>
|
|
19
19
|
#include <netbase.h>
|
|
20
20
|
#include <netmessagemaker.h>
|
|
21
21
|
#include <node/blockstorage.h>
|
|
22
|
-
#include <policy/fees.h>
|
|
23
22
|
#include <policy/policy.h>
|
|
24
23
|
#include <primitives/block.h>
|
|
25
24
|
#include <primitives/transaction.h>
|
|
@@ -45,10 +44,11 @@
|
|
|
45
44
|
#include <optional>
|
|
46
45
|
#include <typeinfo>
|
|
47
46
|
|
|
47
|
+
#include <kernel.h>
|
|
48
|
+
|
|
48
49
|
using node::ReadBlockFromDisk;
|
|
49
50
|
using node::ReadRawBlockFromDisk;
|
|
50
51
|
using node::fImporting;
|
|
51
|
-
using node::fPruneMode;
|
|
52
52
|
using node::fReindex;
|
|
53
53
|
|
|
54
54
|
/** How long to cache transactions in mapRelay for normal relay */
|
|
@@ -415,7 +415,7 @@ private:
|
|
|
415
415
|
void RelayAddress(NodeId originator, const CAddress& addr, bool fReachable);
|
|
416
416
|
|
|
417
417
|
/** Send `feefilter` message. */
|
|
418
|
-
void MaybeSendFeefilter(CNode& node, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
418
|
+
// void MaybeSendFeefilter(CNode& node, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
419
419
|
|
|
420
420
|
const CChainParams& m_chainparams;
|
|
421
421
|
CConnman& m_connman;
|
|
@@ -594,6 +594,13 @@ private:
|
|
|
594
594
|
TxOrphanage m_orphanage;
|
|
595
595
|
|
|
596
596
|
void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
|
|
597
|
+
/** peercoin: blocks that are waiting to be processed, the key points to previous CBlockIndex entry */
|
|
598
|
+
struct WaitElement {
|
|
599
|
+
std::shared_ptr<CBlock> pblock;
|
|
600
|
+
int64_t time;
|
|
601
|
+
};
|
|
602
|
+
std::map<CBlockIndex*, WaitElement> mapBlocksWait;
|
|
603
|
+
|
|
597
604
|
|
|
598
605
|
/** Orphan/conflicted/etc transactions that are kept for compact block reconstruction.
|
|
599
606
|
* The last -blockreconstructionextratxn/DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN of
|
|
@@ -1004,8 +1011,8 @@ void PeerManagerImpl::ProcessBlockAvailability(NodeId nodeid) {
|
|
|
1004
1011
|
|
|
1005
1012
|
if (!state->hashLastUnknownBlock.IsNull()) {
|
|
1006
1013
|
const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock);
|
|
1007
|
-
if (pindex && pindex->
|
|
1008
|
-
if (state->pindexBestKnownBlock == nullptr || pindex->
|
|
1014
|
+
if (pindex && pindex->nChainTrust > 0) {
|
|
1015
|
+
if (state->pindexBestKnownBlock == nullptr || pindex->nChainTrust >= state->pindexBestKnownBlock->nChainTrust) {
|
|
1009
1016
|
state->pindexBestKnownBlock = pindex;
|
|
1010
1017
|
}
|
|
1011
1018
|
state->hashLastUnknownBlock.SetNull();
|
|
@@ -1020,9 +1027,9 @@ void PeerManagerImpl::UpdateBlockAvailability(NodeId nodeid, const uint256 &hash
|
|
|
1020
1027
|
ProcessBlockAvailability(nodeid);
|
|
1021
1028
|
|
|
1022
1029
|
const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hash);
|
|
1023
|
-
if (pindex && pindex->
|
|
1030
|
+
if (pindex && pindex->nChainTrust > 0) {
|
|
1024
1031
|
// An actually better block was announced.
|
|
1025
|
-
if (state->pindexBestKnownBlock == nullptr || pindex->
|
|
1032
|
+
if (state->pindexBestKnownBlock == nullptr || pindex->nChainTrust >= state->pindexBestKnownBlock->nChainTrust) {
|
|
1026
1033
|
state->pindexBestKnownBlock = pindex;
|
|
1027
1034
|
}
|
|
1028
1035
|
} else {
|
|
@@ -1043,7 +1050,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
|
|
|
1043
1050
|
// Make sure pindexBestKnownBlock is up to date, we'll need it.
|
|
1044
1051
|
ProcessBlockAvailability(nodeid);
|
|
1045
1052
|
|
|
1046
|
-
if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->
|
|
1053
|
+
if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainTrust < m_chainman.ActiveChain().Tip()->nChainTrust || state->pindexBestKnownBlock->nChainTrust < nMinimumChainWork) {
|
|
1047
1054
|
// This peer has nothing interesting.
|
|
1048
1055
|
return;
|
|
1049
1056
|
}
|
|
@@ -1060,7 +1067,6 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
|
|
|
1060
1067
|
if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
|
|
1061
1068
|
return;
|
|
1062
1069
|
|
|
1063
|
-
const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
|
|
1064
1070
|
std::vector<const CBlockIndex*> vToFetch;
|
|
1065
1071
|
const CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
|
|
1066
1072
|
// Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
|
|
@@ -1084,13 +1090,13 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
|
|
|
1084
1090
|
// Iterate over those blocks in vToFetch (in forward direction), adding the ones that
|
|
1085
1091
|
// are not yet downloaded and not in flight to vBlocks. In the meantime, update
|
|
1086
1092
|
// pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's
|
|
1087
|
-
// already part of our chain
|
|
1093
|
+
// already part of our chain.
|
|
1088
1094
|
for (const CBlockIndex* pindex : vToFetch) {
|
|
1089
1095
|
if (!pindex->IsValid(BLOCK_VALID_TREE)) {
|
|
1090
1096
|
// We consider the chain that this peer is on invalid.
|
|
1091
1097
|
return;
|
|
1092
1098
|
}
|
|
1093
|
-
if (!State(nodeid)->fHaveWitness &&
|
|
1099
|
+
if (!State(nodeid)->fHaveWitness && IsBTC16BIPsEnabled(pindex->nTime)) {
|
|
1094
1100
|
// We wouldn't download this block or its descendants from this peer.
|
|
1095
1101
|
return;
|
|
1096
1102
|
}
|
|
@@ -1594,7 +1600,7 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha
|
|
|
1594
1600
|
return;
|
|
1595
1601
|
nHighestFastAnnounce = pindex->nHeight;
|
|
1596
1602
|
|
|
1597
|
-
bool fWitnessEnabled =
|
|
1603
|
+
bool fWitnessEnabled = IsBTC16BIPsEnabled(pindex->nTime);
|
|
1598
1604
|
uint256 hashBlock(pblock->GetHash());
|
|
1599
1605
|
|
|
1600
1606
|
{
|
|
@@ -1870,15 +1876,14 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
|
|
1870
1876
|
pfrom.fDisconnect = true;
|
|
1871
1877
|
return;
|
|
1872
1878
|
}
|
|
1873
|
-
//
|
|
1874
|
-
// it's available before trying to send.
|
|
1879
|
+
// Check whether the block is available before trying to send.
|
|
1875
1880
|
if (!(pindex->nStatus & BLOCK_HAVE_DATA)) {
|
|
1876
1881
|
return;
|
|
1877
1882
|
}
|
|
1878
1883
|
std::shared_ptr<const CBlock> pblock;
|
|
1879
1884
|
if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
|
|
1880
1885
|
pblock = a_recent_block;
|
|
1881
|
-
} else if (inv.IsMsgWitnessBlk()) {
|
|
1886
|
+
} /* else if (inv.IsMsgWitnessBlk()) {
|
|
1882
1887
|
// Fast-path: in this case it is possible to serve the block directly from disk,
|
|
1883
1888
|
// as the network format matches the format on disk
|
|
1884
1889
|
std::vector<uint8_t> block_data;
|
|
@@ -1887,7 +1892,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
|
|
1887
1892
|
}
|
|
1888
1893
|
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, Span{block_data}));
|
|
1889
1894
|
// Don't set pblock as we've sent the block
|
|
1890
|
-
} else {
|
|
1895
|
+
} */ else {
|
|
1891
1896
|
// Send block from disk
|
|
1892
1897
|
std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();
|
|
1893
1898
|
if (!ReadBlockFromDisk(*pblockRead, pindex, m_chainparams.GetConsensus())) {
|
|
@@ -1951,8 +1956,11 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
|
|
1951
1956
|
// Send immediately. This must send even if redundant,
|
|
1952
1957
|
// and we want it right after the last block so they don't
|
|
1953
1958
|
// wait for other stuff first.
|
|
1959
|
+
// peercoin: send latest proof-of-work block to allow the
|
|
1960
|
+
// download node to accept as orphan (proof-of-stake
|
|
1961
|
+
// block might be rejected by stake connection check)
|
|
1954
1962
|
std::vector<CInv> vInv;
|
|
1955
|
-
vInv.push_back(CInv(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash()));
|
|
1963
|
+
vInv.push_back(CInv(MSG_BLOCK, GetLastBlockIndex(m_chainman.ActiveChain().Tip(), false)->GetBlockHash()));
|
|
1956
1964
|
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv));
|
|
1957
1965
|
peer.m_continuation_block.SetNull();
|
|
1958
1966
|
}
|
|
@@ -2164,13 +2172,15 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
|
|
|
2164
2172
|
}
|
|
2165
2173
|
}
|
|
2166
2174
|
|
|
2175
|
+
int32_t& nPoSTemperature = mapPoSTemperature[pfrom.addr];
|
|
2167
2176
|
BlockValidationState state;
|
|
2168
|
-
if (!m_chainman.ProcessNewBlockHeaders(headers, state, m_chainparams, &pindexLast)) {
|
|
2177
|
+
if (!m_chainman.ProcessNewBlockHeaders(nPoSTemperature, pfrom.lastAcceptedHeader, headers, state, m_chainparams, &pindexLast)) {
|
|
2169
2178
|
if (state.IsInvalid()) {
|
|
2170
2179
|
MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received");
|
|
2171
2180
|
return;
|
|
2172
2181
|
}
|
|
2173
2182
|
}
|
|
2183
|
+
pfrom.lastAcceptedHeader = headers.back().GetHash();
|
|
2174
2184
|
|
|
2175
2185
|
{
|
|
2176
2186
|
LOCK(cs_main);
|
|
@@ -2187,7 +2197,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
|
|
|
2187
2197
|
// because it is set in UpdateBlockAvailability. Some nullptr checks
|
|
2188
2198
|
// are still present, however, as belt-and-suspenders.
|
|
2189
2199
|
|
|
2190
|
-
if (received_new_header && pindexLast->
|
|
2200
|
+
if (received_new_header && pindexLast->nChainTrust > m_chainman.ActiveChain().Tip()->nChainTrust) {
|
|
2191
2201
|
nodestate->m_last_block_announcement = GetTime();
|
|
2192
2202
|
}
|
|
2193
2203
|
|
|
@@ -2202,14 +2212,14 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
|
|
|
2202
2212
|
|
|
2203
2213
|
// If this set of headers is valid and ends in a block with at least as
|
|
2204
2214
|
// much work as our tip, download as much as possible.
|
|
2205
|
-
if (CanDirectFetch() && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->
|
|
2215
|
+
if (CanDirectFetch() && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainTrust <= pindexLast->nChainTrust) {
|
|
2206
2216
|
std::vector<const CBlockIndex*> vToFetch;
|
|
2207
2217
|
const CBlockIndex *pindexWalk = pindexLast;
|
|
2208
2218
|
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
|
|
2209
2219
|
while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
|
|
2210
2220
|
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
|
|
2211
2221
|
!IsBlockRequested(pindexWalk->GetBlockHash()) &&
|
|
2212
|
-
(!
|
|
2222
|
+
(!IsBTC16BIPsEnabled(pindexWalk->nTime) || State(pfrom.GetId())->fHaveWitness)) {
|
|
2213
2223
|
// We don't have this block, and it's not yet in flight.
|
|
2214
2224
|
vToFetch.push_back(pindexWalk);
|
|
2215
2225
|
}
|
|
@@ -2259,7 +2269,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
|
|
|
2259
2269
|
if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
|
|
2260
2270
|
// When nCount < MAX_HEADERS_RESULTS, we know we have no more
|
|
2261
2271
|
// headers to fetch from this peer.
|
|
2262
|
-
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->
|
|
2272
|
+
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainTrust < nMinimumChainWork) {
|
|
2263
2273
|
// This peer has too little work on their headers chain to help
|
|
2264
2274
|
// us sync -- disconnect if it is an outbound disconnection
|
|
2265
2275
|
// candidate.
|
|
@@ -2281,7 +2291,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
|
|
|
2281
2291
|
// thus always subject to eviction under the bad/lagging chain logic.
|
|
2282
2292
|
// See ChainSyncTimeoutState.
|
|
2283
2293
|
if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) {
|
|
2284
|
-
if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->
|
|
2294
|
+
if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainTrust >= m_chainman.ActiveChain().Tip()->nChainTrust && !nodestate->m_chain_sync.m_protect) {
|
|
2285
2295
|
LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
|
|
2286
2296
|
nodestate->m_chain_sync.m_protect = true;
|
|
2287
2297
|
++m_outbound_peers_with_protect_from_disconnect;
|
|
@@ -2560,9 +2570,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
2560
2570
|
PeerRef peer = GetPeerRef(pfrom.GetId());
|
|
2561
2571
|
if (peer == nullptr) return;
|
|
2562
2572
|
|
|
2573
|
+
// set deserialization mode to read PoS flag in headers
|
|
2574
|
+
vRecv.SetType(vRecv.GetType() | SER_POSMARKER);
|
|
2575
|
+
|
|
2563
2576
|
if (msg_type == NetMsgType::VERSION) {
|
|
2564
|
-
|
|
2565
|
-
|
|
2577
|
+
auto it = mapPoSTemperature.find(pfrom.addr);
|
|
2578
|
+
if (it == mapPoSTemperature.end())
|
|
2579
|
+
mapPoSTemperature[pfrom.addr] = MAX_CONSECUTIVE_POS_HEADERS/4;
|
|
2580
|
+
// Each connection can only send one version message
|
|
2581
|
+
if (pfrom.nVersion != 0)
|
|
2582
|
+
{
|
|
2583
|
+
Misbehaving(pfrom.GetId(), 1, "redundant version message");
|
|
2566
2584
|
return;
|
|
2567
2585
|
}
|
|
2568
2586
|
|
|
@@ -3016,13 +3034,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3016
3034
|
for (CInv& inv : vInv) {
|
|
3017
3035
|
if (interruptMsgProc) return;
|
|
3018
3036
|
|
|
3019
|
-
//
|
|
3020
|
-
// Note that orphan parent fetching always uses MSG_TX GETDATAs regardless of the wtxidrelay setting.
|
|
3021
|
-
// This is fine as no INV messages are involved in that process.
|
|
3037
|
+
// ignore INVs that don't match wtxidrelay setting
|
|
3022
3038
|
if (State(pfrom.GetId())->m_wtxid_relay) {
|
|
3023
|
-
if (inv.
|
|
3039
|
+
if (inv.type == MSG_TX) continue;
|
|
3024
3040
|
} else {
|
|
3025
|
-
if (inv.
|
|
3041
|
+
if (inv.type == MSG_WTX) continue;
|
|
3026
3042
|
}
|
|
3027
3043
|
|
|
3028
3044
|
if (inv.IsMsgBlk()) {
|
|
@@ -3134,14 +3150,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3134
3150
|
if (pindex->GetBlockHash() == hashStop)
|
|
3135
3151
|
{
|
|
3136
3152
|
LogPrint(BCLog::NET, " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / m_chainparams.GetConsensus().nPowTargetSpacing;
|
|
3142
|
-
if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= m_chainman.ActiveChain().Tip()->nHeight - nPrunedBlocksLikelyToHave))
|
|
3143
|
-
{
|
|
3144
|
-
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
|
|
3153
|
+
// peercoin: tell downloading node about the latest block if it's
|
|
3154
|
+
// without risk being rejected due to stake connection check
|
|
3155
|
+
if (hashStop != m_chainman.ActiveChain().Tip()->GetBlockHash() && pindex->GetBlockTime() + Params().GetConsensus().nStakeMinAge > m_chainman.ActiveChain().Tip()->GetBlockTime())
|
|
3156
|
+
WITH_LOCK(peer->m_block_inv_mutex, peer->m_blocks_for_inv_relay.push_back(pindex->GetBlockHash()));
|
|
3145
3157
|
break;
|
|
3146
3158
|
}
|
|
3147
3159
|
WITH_LOCK(peer->m_block_inv_mutex, peer->m_blocks_for_inv_relay.push_back(pindex->GetBlockHash()));
|
|
@@ -3517,15 +3529,24 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3517
3529
|
}
|
|
3518
3530
|
}
|
|
3519
3531
|
|
|
3532
|
+
int32_t& nPoSTemperature = mapPoSTemperature[pfrom.addr];
|
|
3520
3533
|
const CBlockIndex *pindex = nullptr;
|
|
3521
3534
|
BlockValidationState state;
|
|
3522
|
-
if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, m_chainparams, &pindex)) {
|
|
3535
|
+
if (!m_chainman.ProcessNewBlockHeaders(nPoSTemperature, m_chainman.ActiveChain().Tip()->GetBlockHash(), {cmpctblock.header}, state, m_chainparams, &pindex)) {
|
|
3523
3536
|
if (state.IsInvalid()) {
|
|
3524
3537
|
MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
|
|
3525
3538
|
return;
|
|
3526
3539
|
}
|
|
3527
3540
|
}
|
|
3528
3541
|
|
|
3542
|
+
if (nPoSTemperature >= MAX_CONSECUTIVE_POS_HEADERS) {
|
|
3543
|
+
nPoSTemperature = (MAX_CONSECUTIVE_POS_HEADERS*3)/4;
|
|
3544
|
+
if (Params().NetworkIDString() != "test") {
|
|
3545
|
+
Misbehaving(pfrom.GetId(), 100, "too many consecutive pos headers");
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3529
3550
|
// When we succeed in decoding a block's txids from a cmpctblock
|
|
3530
3551
|
// message we typically jump to the BLOCKTXN handling code, with a
|
|
3531
3552
|
// dummy (empty) BLOCKTXN message, to re-use the logic there in
|
|
@@ -3552,7 +3573,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3552
3573
|
|
|
3553
3574
|
// If this was a new header with more work than our tip, update the
|
|
3554
3575
|
// peer's last block announcement time
|
|
3555
|
-
if (received_new_header && pindex->
|
|
3576
|
+
if (received_new_header && pindex->nChainTrust > m_chainman.ActiveChain().Tip()->nChainTrust) {
|
|
3556
3577
|
nodestate->m_last_block_announcement = GetTime();
|
|
3557
3578
|
}
|
|
3558
3579
|
|
|
@@ -3562,8 +3583,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3562
3583
|
if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
|
|
3563
3584
|
return;
|
|
3564
3585
|
|
|
3565
|
-
if (pindex->
|
|
3566
|
-
pindex->nTx != 0) { // We had this block at some point
|
|
3586
|
+
if (pindex->nChainTrust <= m_chainman.ActiveChain().Tip()->nChainTrust || // We know something better
|
|
3587
|
+
pindex->nTx != 0) { // We had this block at some point
|
|
3567
3588
|
if (fAlreadyInFlight) {
|
|
3568
3589
|
// We requested this block for some reason, but our mempool will probably be useless
|
|
3569
3590
|
// so we just grab the block via normal getdata
|
|
@@ -3579,7 +3600,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3579
3600
|
return;
|
|
3580
3601
|
}
|
|
3581
3602
|
|
|
3582
|
-
if (
|
|
3603
|
+
if (IsBTC16BIPsEnabled(pindex->nTime) && !nodestate->fSupportsDesiredCmpctVersion) {
|
|
3583
3604
|
// Don't bother trying to process compact blocks from v1 peers
|
|
3584
3605
|
// after segwit activates.
|
|
3585
3606
|
return;
|
|
@@ -3797,9 +3818,32 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3797
3818
|
return;
|
|
3798
3819
|
}
|
|
3799
3820
|
headers.resize(nCount);
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3821
|
+
{
|
|
3822
|
+
LOCK(cs_main);
|
|
3823
|
+
int32_t& nPoSTemperature = mapPoSTemperature[pfrom.addr];
|
|
3824
|
+
int nTmpPoSTemperature = nPoSTemperature;
|
|
3825
|
+
for (unsigned int n = 0; n < nCount; n++) {
|
|
3826
|
+
vRecv >> headers[n];
|
|
3827
|
+
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
|
|
3828
|
+
ReadCompactSize(vRecv); // needed for vchBlockSig.
|
|
3829
|
+
|
|
3830
|
+
// peercoin: quick check to see if we should ban peers for PoS spam
|
|
3831
|
+
// note: at this point we don't know if PoW headers are valid - we just assume they are
|
|
3832
|
+
// so we need to update pfrom->nPoSTemperature once we actualy check them
|
|
3833
|
+
bool fPoS = headers[n].nFlags & CBlockIndex::BLOCK_PROOF_OF_STAKE;
|
|
3834
|
+
nTmpPoSTemperature += fPoS ? 1 : -POW_HEADER_COOLING;
|
|
3835
|
+
// peer cannot cool himself by PoW headers from other branches
|
|
3836
|
+
if (n == 0 && !fPoS && headers[n].hashPrevBlock != pfrom.lastAcceptedHeader)
|
|
3837
|
+
nTmpPoSTemperature += POW_HEADER_COOLING;
|
|
3838
|
+
nTmpPoSTemperature = std::max(nTmpPoSTemperature, 0);
|
|
3839
|
+
if (nTmpPoSTemperature >= MAX_CONSECUTIVE_POS_HEADERS) {
|
|
3840
|
+
nPoSTemperature = (MAX_CONSECUTIVE_POS_HEADERS*3)/4;
|
|
3841
|
+
if (Params().NetworkIDString() != "test") {
|
|
3842
|
+
Misbehaving(pfrom.GetId(), 100, "too many consecutive pos headers");
|
|
3843
|
+
return;
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3803
3847
|
}
|
|
3804
3848
|
|
|
3805
3849
|
return ProcessHeadersMessage(pfrom, *peer, headers, /*via_compact_block=*/false);
|
|
@@ -3813,25 +3857,115 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3813
3857
|
return;
|
|
3814
3858
|
}
|
|
3815
3859
|
|
|
3816
|
-
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
|
|
3817
|
-
vRecv >> *pblock;
|
|
3818
3860
|
|
|
3819
|
-
|
|
3861
|
+
std::shared_ptr<CBlock> pblock2 = std::make_shared<CBlock>();
|
|
3862
|
+
vRecv >> *pblock2;
|
|
3863
|
+
int64_t nTimeNow = GetTimeSeconds();
|
|
3864
|
+
|
|
3865
|
+
LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock2->GetHash().ToString(), pfrom.GetId());
|
|
3820
3866
|
|
|
3821
|
-
bool forceProcessing = false;
|
|
3822
|
-
const uint256 hash(pblock->GetHash());
|
|
3823
3867
|
{
|
|
3868
|
+
const uint256 hash2(pblock2->GetHash());
|
|
3824
3869
|
LOCK(cs_main);
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3870
|
+
bool fRequested = mapBlocksInFlight.count(hash2);
|
|
3871
|
+
|
|
3872
|
+
CBlockIndex* headerPrev = m_chainman.m_blockman.LookupBlockIndex(pblock2->hashPrevBlock);
|
|
3873
|
+
if (!headerPrev) {
|
|
3874
|
+
LogPrint(BCLog::NET, "previous header not found");
|
|
3875
|
+
return;
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
if (!fRequested) {
|
|
3879
|
+
int32_t& nPoSTemperature = mapPoSTemperature[pfrom.addr];
|
|
3880
|
+
if (nPoSTemperature >= MAX_CONSECUTIVE_POS_HEADERS) {
|
|
3881
|
+
nPoSTemperature = (MAX_CONSECUTIVE_POS_HEADERS*3)/4;
|
|
3882
|
+
if (Params().NetworkIDString() != "test") {
|
|
3883
|
+
Misbehaving(pfrom.GetId(), 100, "too many consecutive pos headers");
|
|
3884
|
+
return;
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3888
|
+
if (pblock2->IsProofOfStake() && !m_chainman.ActiveChainstate().IsInitialBlockDownload())
|
|
3889
|
+
nPoSTemperature += 1;
|
|
3890
|
+
|
|
3891
|
+
if (!headerPrev->IsValid(BLOCK_VALID_TRANSACTIONS)) {
|
|
3892
|
+
RemoveBlockRequest(hash2);
|
|
3893
|
+
LogPrint(BCLog::NET, "this block does not connect to any valid known blocks");
|
|
3894
|
+
return;
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
// peercoin: store in memory until we can connect it to some chain
|
|
3898
|
+
WaitElement we; we.pblock = pblock2; we.time = nTimeNow;
|
|
3899
|
+
mapBlocksWait[headerPrev] = we;
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
static CBlockIndex* pindexLastAccepted = nullptr;
|
|
3903
|
+
if (pindexLastAccepted == nullptr)
|
|
3904
|
+
pindexLastAccepted = m_chainman.ActiveChain().Tip();
|
|
3905
|
+
bool fContinue = true;
|
|
3906
|
+
|
|
3907
|
+
// peercoin: accept as many blocks as we possibly can from mapBlocksWait
|
|
3908
|
+
while (fContinue) {
|
|
3909
|
+
fContinue = false;
|
|
3910
|
+
bool fSelected = false;
|
|
3911
|
+
bool forceProcessing = false;
|
|
3912
|
+
CBlockIndex* pindexPrev;
|
|
3913
|
+
std::shared_ptr<CBlock> pblock;
|
|
3914
|
+
|
|
3915
|
+
{
|
|
3916
|
+
LOCK(cs_main);
|
|
3917
|
+
// peercoin: try to select next block in a constant time
|
|
3918
|
+
std::map<CBlockIndex*, WaitElement>::iterator it = mapBlocksWait.find(pindexLastAccepted);
|
|
3919
|
+
if (it != mapBlocksWait.end() && pindexLastAccepted != nullptr) {
|
|
3920
|
+
pindexPrev = it->first;
|
|
3921
|
+
pblock = it->second.pblock;
|
|
3922
|
+
mapBlocksWait.erase(pindexPrev);
|
|
3923
|
+
fContinue = true;
|
|
3924
|
+
fSelected = true;
|
|
3925
|
+
} else
|
|
3926
|
+
// otherwise: try to scan for it
|
|
3927
|
+
for (auto& pair : mapBlocksWait) {
|
|
3928
|
+
pindexPrev = pair.first;
|
|
3929
|
+
pblock = pair.second.pblock;
|
|
3930
|
+
const uint256 hash(pblock->GetHash());
|
|
3931
|
+
// remove blocks that were not connected in 60 seconds
|
|
3932
|
+
if (nTimeNow > pair.second.time + 60) {
|
|
3933
|
+
mapBlocksWait.erase(pindexPrev);
|
|
3934
|
+
fContinue = true;
|
|
3935
|
+
RemoveBlockRequest(hash);
|
|
3936
|
+
break;
|
|
3937
|
+
}
|
|
3938
|
+
if (!pindexPrev->IsValid(BLOCK_VALID_TRANSACTIONS)) {
|
|
3939
|
+
if (pindexPrev->nStatus & BLOCK_FAILED_MASK) {
|
|
3940
|
+
mapBlocksWait.erase(pindexPrev); // prev block was rejected
|
|
3941
|
+
fContinue = true;
|
|
3942
|
+
RemoveBlockRequest(hash);
|
|
3943
|
+
break;
|
|
3944
|
+
}
|
|
3945
|
+
continue; // prev block was not (yet) accepted on disk, skip to next one
|
|
3946
|
+
}
|
|
3947
|
+
|
|
3948
|
+
mapBlocksWait.erase(pindexPrev);
|
|
3949
|
+
fContinue = true;
|
|
3950
|
+
fSelected = true;
|
|
3951
|
+
break;
|
|
3952
|
+
}
|
|
3953
|
+
if (!fSelected)
|
|
3954
|
+
continue;
|
|
3955
|
+
|
|
3956
|
+
const uint256 hash(pblock->GetHash());
|
|
3957
|
+
// Always process the block if we requested it, since we may
|
|
3958
|
+
// need it even when it's not a candidate for a new best tip.
|
|
3959
|
+
forceProcessing = IsBlockRequested(hash);
|
|
3960
|
+
RemoveBlockRequest(hash);
|
|
3961
|
+
// mapBlockSource is only used for punishing peers and setting
|
|
3962
|
+
// which peers send us compact blocks, so the race between here and
|
|
3963
|
+
// cs_main in ProcessNewBlock is fine.
|
|
3964
|
+
mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
|
|
3965
|
+
}
|
|
3966
|
+
|
|
3967
|
+
ProcessBlock(pfrom, pblock, forceProcessing);
|
|
3968
|
+
}
|
|
3835
3969
|
return;
|
|
3836
3970
|
}
|
|
3837
3971
|
|
|
@@ -4048,7 +4182,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
4048
4182
|
if (pfrom.m_tx_relay != nullptr) {
|
|
4049
4183
|
pfrom.m_tx_relay->minFeeFilter = newFeeFilter;
|
|
4050
4184
|
}
|
|
4051
|
-
LogPrint(BCLog::NET, "received: feefilter of %
|
|
4185
|
+
LogPrint(BCLog::NET, "received: feefilter of %d satoshi from peer=%d\n", newFeeFilter, pfrom.GetId());
|
|
4052
4186
|
}
|
|
4053
4187
|
return;
|
|
4054
4188
|
}
|
|
@@ -4083,7 +4217,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
4083
4217
|
}
|
|
4084
4218
|
return;
|
|
4085
4219
|
}
|
|
4086
|
-
|
|
4087
4220
|
// Ignore unknown commands for extensibility
|
|
4088
4221
|
LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom.GetId());
|
|
4089
4222
|
return;
|
|
@@ -4224,13 +4357,13 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, std::chrono::seconds time_in_
|
|
|
4224
4357
|
// their chain has more work than ours, we should sync to it,
|
|
4225
4358
|
// unless it's invalid, in which case we should find that out and
|
|
4226
4359
|
// disconnect from them elsewhere).
|
|
4227
|
-
if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->
|
|
4360
|
+
if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainTrust >= m_chainman.ActiveChain().Tip()->nChainTrust) {
|
|
4228
4361
|
if (state.m_chain_sync.m_timeout != 0s) {
|
|
4229
4362
|
state.m_chain_sync.m_timeout = 0s;
|
|
4230
4363
|
state.m_chain_sync.m_work_header = nullptr;
|
|
4231
4364
|
state.m_chain_sync.m_sent_getheaders = false;
|
|
4232
4365
|
}
|
|
4233
|
-
} else if (state.m_chain_sync.m_timeout == 0s || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->
|
|
4366
|
+
} else if (state.m_chain_sync.m_timeout == 0s || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainTrust >= state.m_chain_sync.m_work_header->nChainTrust)) {
|
|
4234
4367
|
// Our best block known by this peer is behind our tip, and we're either noticing
|
|
4235
4368
|
// that for the first time, OR this peer was able to catch up to some earlier point
|
|
4236
4369
|
// where we checked against our tip.
|
|
@@ -4504,6 +4637,7 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros
|
|
|
4504
4637
|
}
|
|
4505
4638
|
}
|
|
4506
4639
|
|
|
4640
|
+
/*
|
|
4507
4641
|
void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, std::chrono::microseconds current_time)
|
|
4508
4642
|
{
|
|
4509
4643
|
AssertLockHeld(cs_main);
|
|
@@ -4546,6 +4680,7 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, std::chrono::microseconds c
|
|
|
4546
4680
|
pto.m_tx_relay->m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
|
|
4547
4681
|
}
|
|
4548
4682
|
}
|
|
4683
|
+
*/
|
|
4549
4684
|
|
|
4550
4685
|
namespace {
|
|
4551
4686
|
class CompareInvMempoolOrder
|
|
@@ -4833,7 +4968,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
4833
4968
|
if (fSendTrickle && pto->m_tx_relay->fSendMempool) {
|
|
4834
4969
|
auto vtxinfo = m_mempool.infoAll();
|
|
4835
4970
|
pto->m_tx_relay->fSendMempool = false;
|
|
4836
|
-
|
|
4971
|
+
CAmount filterrate = 0;
|
|
4837
4972
|
|
|
4838
4973
|
LOCK(pto->m_tx_relay->cs_filter);
|
|
4839
4974
|
|
|
@@ -4841,9 +4976,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
4841
4976
|
const uint256& hash = state.m_wtxid_relay ? txinfo.tx->GetWitnessHash() : txinfo.tx->GetHash();
|
|
4842
4977
|
CInv inv(state.m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
|
|
4843
4978
|
pto->m_tx_relay->setInventoryTxToSend.erase(hash);
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4979
|
+
if (filterrate) {
|
|
4980
|
+
if (txinfo.fee < filterrate)
|
|
4981
|
+
continue;
|
|
4847
4982
|
}
|
|
4848
4983
|
if (pto->m_tx_relay->pfilter) {
|
|
4849
4984
|
if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
|
@@ -4867,7 +5002,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
4867
5002
|
for (std::set<uint256>::iterator it = pto->m_tx_relay->setInventoryTxToSend.begin(); it != pto->m_tx_relay->setInventoryTxToSend.end(); it++) {
|
|
4868
5003
|
vInvTx.push_back(it);
|
|
4869
5004
|
}
|
|
4870
|
-
|
|
5005
|
+
CAmount filterrate = 0;
|
|
4871
5006
|
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
|
|
4872
5007
|
// A heap is used so that not all items need sorting if only a few are being sent.
|
|
4873
5008
|
CompareInvMempoolOrder compareInvMempoolOrder(&m_mempool, state.m_wtxid_relay);
|
|
@@ -4897,7 +5032,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
4897
5032
|
auto txid = txinfo.tx->GetHash();
|
|
4898
5033
|
auto wtxid = txinfo.tx->GetWitnessHash();
|
|
4899
5034
|
// Peer told you to not send transactions at that feerate? Don't bother sending it.
|
|
4900
|
-
if (txinfo.fee < filterrate
|
|
5035
|
+
if (filterrate && txinfo.fee < filterrate) {
|
|
4901
5036
|
continue;
|
|
4902
5037
|
}
|
|
4903
5038
|
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
|
@@ -5011,7 +5146,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
5011
5146
|
NodeId staller = -1;
|
|
5012
5147
|
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
|
|
5013
5148
|
for (const CBlockIndex *pindex : vToDownload) {
|
|
5014
|
-
uint32_t nFetchFlags = GetFetchFlags(*pto);
|
|
5149
|
+
uint32_t nFetchFlags = IsBTC16BIPsEnabled(pindex->nTime) ? GetFetchFlags(*pto) : false;
|
|
5015
5150
|
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
|
|
5016
5151
|
BlockRequested(pto->GetId(), *pindex);
|
|
5017
5152
|
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
|
|
@@ -5051,11 +5186,8 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
5051
5186
|
}
|
|
5052
5187
|
}
|
|
5053
5188
|
|
|
5054
|
-
|
|
5055
5189
|
if (!vGetData.empty())
|
|
5056
5190
|
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
|
|
5057
|
-
|
|
5058
|
-
MaybeSendFeefilter(*pto, current_time);
|
|
5059
5191
|
} // release cs_main
|
|
5060
5192
|
return true;
|
|
5061
5193
|
}
|
|
@@ -19,7 +19,7 @@ public:
|
|
|
19
19
|
{
|
|
20
20
|
CSerializedNetMsg msg;
|
|
21
21
|
msg.m_type = std::move(msg_type);
|
|
22
|
-
CVectorWriter{ SER_NETWORK, nFlags | nVersion, msg.data, 0, std::forward<Args>(args)... };
|
|
22
|
+
CVectorWriter{ SER_NETWORK | SER_POSMARKER, nFlags | nVersion, msg.data, 0, std::forward<Args>(args)... };
|
|
23
23
|
return msg;
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include <flatfile.h>
|
|
12
12
|
#include <fs.h>
|
|
13
13
|
#include <hash.h>
|
|
14
|
+
#include <kernel.h>
|
|
14
15
|
#include <pow.h>
|
|
15
16
|
#include <reverse_iterator.h>
|
|
16
17
|
#include <shutdown.h>
|
|
@@ -24,9 +25,6 @@
|
|
|
24
25
|
namespace node {
|
|
25
26
|
std::atomic_bool fImporting(false);
|
|
26
27
|
std::atomic_bool fReindex(false);
|
|
27
|
-
bool fHavePruned = false;
|
|
28
|
-
bool fPruneMode = false;
|
|
29
|
-
uint64_t nPruneTarget = 0;
|
|
30
28
|
|
|
31
29
|
static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
|
|
32
30
|
static FlatFileSeq BlockFileSeq();
|
|
@@ -65,9 +63,11 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
|
|
|
65
63
|
pindexNew->BuildSkip();
|
|
66
64
|
}
|
|
67
65
|
pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime);
|
|
68
|
-
|
|
66
|
+
if (block.nFlags & CBlockIndex::BLOCK_PROOF_OF_STAKE)
|
|
67
|
+
pindexNew->SetProofOfStake();
|
|
68
|
+
pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + GetBlockTrust(*pindexNew);
|
|
69
69
|
pindexNew->RaiseValidity(BLOCK_VALID_TREE);
|
|
70
|
-
if (pindexBestHeader == nullptr || pindexBestHeader->
|
|
70
|
+
if (pindexBestHeader == nullptr || pindexBestHeader->nChainTrust < pindexNew->nChainTrust)
|
|
71
71
|
pindexBestHeader = pindexNew;
|
|
72
72
|
|
|
73
73
|
m_dirty_blockindex.insert(pindexNew);
|
|
@@ -75,122 +75,6 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
|
|
|
75
75
|
return pindexNew;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
void BlockManager::PruneOneBlockFile(const int fileNumber)
|
|
79
|
-
{
|
|
80
|
-
AssertLockHeld(cs_main);
|
|
81
|
-
LOCK(cs_LastBlockFile);
|
|
82
|
-
|
|
83
|
-
for (const auto& entry : m_block_index) {
|
|
84
|
-
CBlockIndex* pindex = entry.second;
|
|
85
|
-
if (pindex->nFile == fileNumber) {
|
|
86
|
-
pindex->nStatus &= ~BLOCK_HAVE_DATA;
|
|
87
|
-
pindex->nStatus &= ~BLOCK_HAVE_UNDO;
|
|
88
|
-
pindex->nFile = 0;
|
|
89
|
-
pindex->nDataPos = 0;
|
|
90
|
-
pindex->nUndoPos = 0;
|
|
91
|
-
m_dirty_blockindex.insert(pindex);
|
|
92
|
-
|
|
93
|
-
// Prune from m_blocks_unlinked -- any block we prune would have
|
|
94
|
-
// to be downloaded again in order to consider its chain, at which
|
|
95
|
-
// point it would be considered as a candidate for
|
|
96
|
-
// m_blocks_unlinked or setBlockIndexCandidates.
|
|
97
|
-
auto range = m_blocks_unlinked.equal_range(pindex->pprev);
|
|
98
|
-
while (range.first != range.second) {
|
|
99
|
-
std::multimap<CBlockIndex*, CBlockIndex*>::iterator _it = range.first;
|
|
100
|
-
range.first++;
|
|
101
|
-
if (_it->second == pindex) {
|
|
102
|
-
m_blocks_unlinked.erase(_it);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
m_blockfile_info[fileNumber].SetNull();
|
|
109
|
-
m_dirty_fileinfo.insert(fileNumber);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
|
|
113
|
-
{
|
|
114
|
-
assert(fPruneMode && nManualPruneHeight > 0);
|
|
115
|
-
|
|
116
|
-
LOCK2(cs_main, cs_LastBlockFile);
|
|
117
|
-
if (chain_tip_height < 0) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
|
|
122
|
-
unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
|
|
123
|
-
int count = 0;
|
|
124
|
-
for (int fileNumber = 0; fileNumber < m_last_blockfile; fileNumber++) {
|
|
125
|
-
if (m_blockfile_info[fileNumber].nSize == 0 || m_blockfile_info[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
PruneOneBlockFile(fileNumber);
|
|
129
|
-
setFilesToPrune.insert(fileNumber);
|
|
130
|
-
count++;
|
|
131
|
-
}
|
|
132
|
-
LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd)
|
|
136
|
-
{
|
|
137
|
-
LOCK2(cs_main, cs_LastBlockFile);
|
|
138
|
-
if (chain_tip_height < 0 || nPruneTarget == 0) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
if ((uint64_t)chain_tip_height <= nPruneAfterHeight) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
unsigned int nLastBlockWeCanPrune{(unsigned)std::min(prune_height, chain_tip_height - static_cast<int>(MIN_BLOCKS_TO_KEEP))};
|
|
146
|
-
uint64_t nCurrentUsage = CalculateCurrentUsage();
|
|
147
|
-
// We don't check to prune until after we've allocated new space for files
|
|
148
|
-
// So we should leave a buffer under our target to account for another allocation
|
|
149
|
-
// before the next pruning.
|
|
150
|
-
uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;
|
|
151
|
-
uint64_t nBytesToPrune;
|
|
152
|
-
int count = 0;
|
|
153
|
-
|
|
154
|
-
if (nCurrentUsage + nBuffer >= nPruneTarget) {
|
|
155
|
-
// On a prune event, the chainstate DB is flushed.
|
|
156
|
-
// To avoid excessive prune events negating the benefit of high dbcache
|
|
157
|
-
// values, we should not prune too rapidly.
|
|
158
|
-
// So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
|
|
159
|
-
if (is_ibd) {
|
|
160
|
-
// Since this is only relevant during IBD, we use a fixed 10%
|
|
161
|
-
nBuffer += nPruneTarget / 10;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
for (int fileNumber = 0; fileNumber < m_last_blockfile; fileNumber++) {
|
|
165
|
-
nBytesToPrune = m_blockfile_info[fileNumber].nSize + m_blockfile_info[fileNumber].nUndoSize;
|
|
166
|
-
|
|
167
|
-
if (m_blockfile_info[fileNumber].nSize == 0) {
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target?
|
|
172
|
-
break;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
|
|
176
|
-
if (m_blockfile_info[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
PruneOneBlockFile(fileNumber);
|
|
181
|
-
// Queue up the files for removal
|
|
182
|
-
setFilesToPrune.insert(fileNumber);
|
|
183
|
-
nCurrentUsage -= nBytesToPrune;
|
|
184
|
-
count++;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
LogPrint(BCLog::PRUNE, "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n",
|
|
189
|
-
nPruneTarget/1024/1024, nCurrentUsage/1024/1024,
|
|
190
|
-
((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024,
|
|
191
|
-
nLastBlockWeCanPrune, count);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
78
|
CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
|
|
195
79
|
{
|
|
196
80
|
AssertLockHeld(cs_main);
|
|
@@ -221,7 +105,7 @@ bool BlockManager::LoadBlockIndex(
|
|
|
221
105
|
return false;
|
|
222
106
|
}
|
|
223
107
|
|
|
224
|
-
// Calculate
|
|
108
|
+
// Calculate nChainTrust
|
|
225
109
|
std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight;
|
|
226
110
|
vSortedByHeight.reserve(m_block_index.size());
|
|
227
111
|
for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
|
|
@@ -253,13 +137,12 @@ bool BlockManager::LoadBlockIndex(
|
|
|
253
137
|
for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight) {
|
|
254
138
|
if (ShutdownRequested()) return false;
|
|
255
139
|
CBlockIndex* pindex = item.second;
|
|
256
|
-
pindex->
|
|
140
|
+
pindex->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + GetBlockTrust(*pindex);
|
|
257
141
|
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
|
|
258
142
|
|
|
259
143
|
// We can link the chain of blocks for which we've received transactions at some point, or
|
|
260
144
|
// blocks that are assumed-valid on the basis of snapshot load (see
|
|
261
145
|
// PopulateAndValidateSnapshot()).
|
|
262
|
-
// Pruned nodes may have deleted the block.
|
|
263
146
|
if (pindex->nTx > 0) {
|
|
264
147
|
if (pindex->pprev) {
|
|
265
148
|
if (pindex->pprev->nChainTx > 0) {
|
|
@@ -310,7 +193,7 @@ bool BlockManager::LoadBlockIndex(
|
|
|
310
193
|
}
|
|
311
194
|
}
|
|
312
195
|
}
|
|
313
|
-
if (pindex->nStatus & BLOCK_FAILED_MASK && (!chainman.m_best_invalid || pindex->
|
|
196
|
+
if (pindex->nStatus & BLOCK_FAILED_MASK && (!chainman.m_best_invalid || pindex->nChainTrust > chainman.m_best_invalid->nChainTrust)) {
|
|
314
197
|
chainman.m_best_invalid = pindex;
|
|
315
198
|
}
|
|
316
199
|
if (pindex->pprev) {
|
|
@@ -318,6 +201,12 @@ bool BlockManager::LoadBlockIndex(
|
|
|
318
201
|
}
|
|
319
202
|
if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == nullptr || CBlockIndexWorkComparator()(pindexBestHeader, pindex)))
|
|
320
203
|
pindexBestHeader = pindex;
|
|
204
|
+
|
|
205
|
+
// peercoin: calculate stake modifier checksum
|
|
206
|
+
pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
|
|
207
|
+
if (chainman.ActiveChain().Contains(pindex))
|
|
208
|
+
if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum))
|
|
209
|
+
return error("LoadBlockIndex() : Failed stake modifier checkpoint height=%d, modifier=0x%016llx", pindex->nHeight, pindex->nStakeModifier);
|
|
321
210
|
}
|
|
322
211
|
|
|
323
212
|
return true;
|
|
@@ -399,12 +288,6 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
|
|
|
399
288
|
}
|
|
400
289
|
}
|
|
401
290
|
|
|
402
|
-
// Check whether we have ever pruned block & undo files
|
|
403
|
-
m_block_tree_db->ReadFlag("prunedblockfiles", fHavePruned);
|
|
404
|
-
if (fHavePruned) {
|
|
405
|
-
LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n");
|
|
406
|
-
}
|
|
407
|
-
|
|
408
291
|
// Check whether we need to continue reindexing
|
|
409
292
|
bool fReindexing = false;
|
|
410
293
|
m_block_tree_db->ReadReindexing(fReindexing);
|
|
@@ -427,55 +310,6 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
|
|
|
427
310
|
return nullptr;
|
|
428
311
|
}
|
|
429
312
|
|
|
430
|
-
bool IsBlockPruned(const CBlockIndex* pblockindex)
|
|
431
|
-
{
|
|
432
|
-
AssertLockHeld(::cs_main);
|
|
433
|
-
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// If we're using -prune with -reindex, then delete block files that will be ignored by the
|
|
437
|
-
// reindex. Since reindexing works by starting at block file 0 and looping until a blockfile
|
|
438
|
-
// is missing, do the same here to delete any later block files after a gap. Also delete all
|
|
439
|
-
// rev files since they'll be rewritten by the reindex anyway. This ensures that m_blockfile_info
|
|
440
|
-
// is in sync with what's actually on disk by the time we start downloading, so that pruning
|
|
441
|
-
// works correctly.
|
|
442
|
-
void CleanupBlockRevFiles()
|
|
443
|
-
{
|
|
444
|
-
std::map<std::string, fs::path> mapBlockFiles;
|
|
445
|
-
|
|
446
|
-
// Glob all blk?????.dat and rev?????.dat files from the blocks directory.
|
|
447
|
-
// Remove the rev files immediately and insert the blk file paths into an
|
|
448
|
-
// ordered map keyed by block file index.
|
|
449
|
-
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
|
|
450
|
-
fs::path blocksdir = gArgs.GetBlocksDirPath();
|
|
451
|
-
for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
|
|
452
|
-
const std::string path = fs::PathToString(it->path().filename());
|
|
453
|
-
if (fs::is_regular_file(*it) &&
|
|
454
|
-
path.length() == 12 &&
|
|
455
|
-
path.substr(8,4) == ".dat")
|
|
456
|
-
{
|
|
457
|
-
if (path.substr(0, 3) == "blk") {
|
|
458
|
-
mapBlockFiles[path.substr(3, 5)] = it->path();
|
|
459
|
-
} else if (path.substr(0, 3) == "rev") {
|
|
460
|
-
remove(it->path());
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// Remove all block files that aren't part of a contiguous set starting at
|
|
466
|
-
// zero by walking the ordered map (keys are block file indices) by
|
|
467
|
-
// keeping a separate counter. Once we hit a gap (or if 0 doesn't exist)
|
|
468
|
-
// start removing block files.
|
|
469
|
-
int nContigCounter = 0;
|
|
470
|
-
for (const std::pair<const std::string, fs::path>& item : mapBlockFiles) {
|
|
471
|
-
if (LocaleIndependentAtoi<int>(item.first) == nContigCounter) {
|
|
472
|
-
nContigCounter++;
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
remove(item.second);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
313
|
CBlockFileInfo* BlockManager::GetBlockFileInfo(size_t n)
|
|
480
314
|
{
|
|
481
315
|
LOCK(cs_LastBlockFile);
|
|
@@ -576,16 +410,6 @@ uint64_t BlockManager::CalculateCurrentUsage()
|
|
|
576
410
|
return retval;
|
|
577
411
|
}
|
|
578
412
|
|
|
579
|
-
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
|
|
580
|
-
{
|
|
581
|
-
for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
|
|
582
|
-
FlatFilePos pos(*it, 0);
|
|
583
|
-
fs::remove(BlockFileSeq().FileName(pos));
|
|
584
|
-
fs::remove(UndoFileSeq().FileName(pos));
|
|
585
|
-
LogPrint(BCLog::BLOCKSTORE, "Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
413
|
static FlatFileSeq BlockFileSeq()
|
|
590
414
|
{
|
|
591
415
|
return FlatFileSeq(gArgs.GetBlocksDirPath(), "blk", gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE);
|
|
@@ -658,9 +482,6 @@ bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigne
|
|
|
658
482
|
if (out_of_space) {
|
|
659
483
|
return AbortNode("Disk space is too low!", _("Disk space is too low!"));
|
|
660
484
|
}
|
|
661
|
-
if (bytes_allocated != 0 && fPruneMode) {
|
|
662
|
-
m_check_for_pruning = true;
|
|
663
|
-
}
|
|
664
485
|
}
|
|
665
486
|
|
|
666
487
|
m_dirty_fileinfo.insert(nFile);
|
|
@@ -682,9 +503,6 @@ bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFileP
|
|
|
682
503
|
if (out_of_space) {
|
|
683
504
|
return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
|
|
684
505
|
}
|
|
685
|
-
if (bytes_allocated != 0 && fPruneMode) {
|
|
686
|
-
m_check_for_pruning = true;
|
|
687
|
-
}
|
|
688
506
|
|
|
689
507
|
return true;
|
|
690
508
|
}
|
|
@@ -760,7 +578,7 @@ bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::P
|
|
|
760
578
|
}
|
|
761
579
|
|
|
762
580
|
// Check the header
|
|
763
|
-
if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) {
|
|
581
|
+
if (block.IsProofOfWork() && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) {
|
|
764
582
|
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
|
|
765
583
|
}
|
|
766
584
|
|
|
@@ -769,6 +587,10 @@ bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::P
|
|
|
769
587
|
return error("ReadBlockFromDisk: Errors in block solution at %s", pos.ToString());
|
|
770
588
|
}
|
|
771
589
|
|
|
590
|
+
// Set flag if proof of stake
|
|
591
|
+
if (block.IsProofOfStake())
|
|
592
|
+
block.nFlags |= CBlockIndex::BLOCK_PROOF_OF_STAKE;
|
|
593
|
+
|
|
772
594
|
return true;
|
|
773
595
|
}
|
|
774
596
|
|
|
@@ -44,13 +44,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
|
|
|
44
44
|
|
|
45
45
|
extern std::atomic_bool fImporting;
|
|
46
46
|
extern std::atomic_bool fReindex;
|
|
47
|
-
/** Pruning-related variables and constants */
|
|
48
|
-
/** True if any block files have ever been pruned. */
|
|
49
|
-
extern bool fHavePruned;
|
|
50
|
-
/** True if we're running in -prune mode. */
|
|
51
|
-
extern bool fPruneMode;
|
|
52
|
-
/** Number of MiB of block files that we're trying to stay below. */
|
|
53
|
-
extern uint64_t nPruneTarget;
|
|
54
47
|
|
|
55
48
|
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
|
|
56
49
|
|
|
@@ -76,37 +69,9 @@ private:
|
|
|
76
69
|
bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown);
|
|
77
70
|
bool FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize);
|
|
78
71
|
|
|
79
|
-
/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
|
|
80
|
-
void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height);
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Prune block and undo files (blk???.dat and rev???.dat) so that the disk space used is less than a user-defined target.
|
|
84
|
-
* The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
|
|
85
|
-
* space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
|
|
86
|
-
* (which in this case means the blockchain must be re-downloaded.)
|
|
87
|
-
*
|
|
88
|
-
* Pruning functions are called from FlushStateToDisk when the m_check_for_pruning flag has been set.
|
|
89
|
-
* Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
|
|
90
|
-
* Pruning cannot take place until the longest chain is at least a certain length (CChainParams::nPruneAfterHeight).
|
|
91
|
-
* Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
|
|
92
|
-
* The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
|
|
93
|
-
* A db flag records the fact that at least some block files have been pruned.
|
|
94
|
-
*
|
|
95
|
-
* @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
|
|
96
|
-
*/
|
|
97
|
-
void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd);
|
|
98
|
-
|
|
99
72
|
RecursiveMutex cs_LastBlockFile;
|
|
100
73
|
std::vector<CBlockFileInfo> m_blockfile_info;
|
|
101
74
|
int m_last_blockfile = 0;
|
|
102
|
-
/** Global flag to indicate we should check to see if there are
|
|
103
|
-
* block/undo files that should be deleted. Set on startup
|
|
104
|
-
* or if we allocate more file space when we're in prune mode
|
|
105
|
-
*/
|
|
106
|
-
bool m_check_for_pruning = false;
|
|
107
|
-
|
|
108
|
-
/** Dirty block index entries. */
|
|
109
|
-
std::set<CBlockIndex*> m_dirty_blockindex;
|
|
110
75
|
|
|
111
76
|
/** Dirty block file entries. */
|
|
112
77
|
std::set<int> m_dirty_fileinfo;
|
|
@@ -116,12 +81,13 @@ public:
|
|
|
116
81
|
|
|
117
82
|
/**
|
|
118
83
|
* All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
|
|
119
|
-
* Pruned nodes may have entries where B is missing data.
|
|
120
84
|
*/
|
|
121
85
|
std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked;
|
|
122
|
-
|
|
123
86
|
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
|
|
124
87
|
|
|
88
|
+
/** Dirty block index entries. */
|
|
89
|
+
std::set<CBlockIndex*> m_dirty_blockindex;
|
|
90
|
+
|
|
125
91
|
bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
|
126
92
|
bool LoadBlockIndexDB(ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
|
127
93
|
|
|
@@ -141,9 +107,6 @@ public:
|
|
|
141
107
|
/** Create a new block index entry for a given block hash */
|
|
142
108
|
CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
143
109
|
|
|
144
|
-
//! Mark one block file as pruned (modify associated database entries)
|
|
145
|
-
void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
146
|
-
|
|
147
110
|
CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
148
111
|
|
|
149
112
|
/** Get block file info entry for one block file */
|
|
@@ -166,21 +129,11 @@ public:
|
|
|
166
129
|
}
|
|
167
130
|
};
|
|
168
131
|
|
|
169
|
-
//! Check whether the block associated with this index entry is pruned or not.
|
|
170
|
-
bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
|
171
|
-
|
|
172
|
-
void CleanupBlockRevFiles();
|
|
173
|
-
|
|
174
132
|
/** Open a block file (blk?????.dat) */
|
|
175
133
|
FILE* OpenBlockFile(const FlatFilePos& pos, bool fReadOnly = false);
|
|
176
134
|
/** Translation to a filesystem path */
|
|
177
135
|
fs::path GetBlockPosFilename(const FlatFilePos& pos);
|
|
178
136
|
|
|
179
|
-
/**
|
|
180
|
-
* Actually unlink the specified files
|
|
181
|
-
*/
|
|
182
|
-
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
|
|
183
|
-
|
|
184
137
|
/** Functions for disk access for blocks */
|
|
185
138
|
bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams);
|
|
186
139
|
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
|
|
@@ -12,7 +12,6 @@ namespace node {
|
|
|
12
12
|
std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
|
|
13
13
|
ChainstateManager& chainman,
|
|
14
14
|
CTxMemPool* mempool,
|
|
15
|
-
bool fPruneMode,
|
|
16
15
|
const Consensus::Params& consensus_params,
|
|
17
16
|
bool fReindexChainState,
|
|
18
17
|
int64_t nBlockTreeDBCache,
|
|
@@ -42,16 +41,11 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
|
|
|
42
41
|
|
|
43
42
|
if (fReset) {
|
|
44
43
|
pblocktree->WriteReindexing(true);
|
|
45
|
-
//If we're reindexing in prune mode, wipe away unusable block files and all undo data files
|
|
46
|
-
if (fPruneMode)
|
|
47
|
-
CleanupBlockRevFiles();
|
|
48
44
|
}
|
|
49
45
|
|
|
50
46
|
if (shutdown_requested && shutdown_requested()) return ChainstateLoadingError::SHUTDOWN_PROBED;
|
|
51
47
|
|
|
52
|
-
// LoadBlockIndex
|
|
53
|
-
// block file from disk.
|
|
54
|
-
// Note that it also sets fReindex based on the disk flag!
|
|
48
|
+
// Note that LoadBlockIndex also sets fReindex based on the disk flag!
|
|
55
49
|
// From here on out fReindex and fReset mean something different!
|
|
56
50
|
if (!chainman.LoadBlockIndex()) {
|
|
57
51
|
if (shutdown_requested && shutdown_requested()) return ChainstateLoadingError::SHUTDOWN_PROBED;
|
|
@@ -63,12 +57,6 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
|
|
|
63
57
|
return ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK;
|
|
64
58
|
}
|
|
65
59
|
|
|
66
|
-
// Check for changed -prune state. What we are concerned about is a user who has pruned blocks
|
|
67
|
-
// in the past, but is now trying to run unpruned.
|
|
68
|
-
if (fHavePruned && !fPruneMode) {
|
|
69
|
-
return ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
60
|
// At this point blocktree args are consistent with what's on disk.
|
|
73
61
|
// If we're not mid-reindex (based on disk + args), add a genesis block on disk
|
|
74
62
|
// (otherwise we use the one already on disk).
|
|
@@ -19,7 +19,6 @@ namespace node {
|
|
|
19
19
|
enum class ChainstateLoadingError {
|
|
20
20
|
ERROR_LOADING_BLOCK_DB,
|
|
21
21
|
ERROR_BAD_GENESIS_BLOCK,
|
|
22
|
-
ERROR_PRUNED_NEEDS_REINDEX,
|
|
23
22
|
ERROR_LOAD_GENESIS_BLOCK_FAILED,
|
|
24
23
|
ERROR_CHAINSTATE_UPGRADE_FAILED,
|
|
25
24
|
ERROR_REPLAYBLOCKS_FAILED,
|
|
@@ -58,7 +57,6 @@ enum class ChainstateLoadingError {
|
|
|
58
57
|
std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
|
|
59
58
|
ChainstateManager& chainman,
|
|
60
59
|
CTxMemPool* mempool,
|
|
61
|
-
bool fPruneMode,
|
|
62
60
|
const Consensus::Params& consensus_params,
|
|
63
61
|
bool fReindexChainState,
|
|
64
62
|
int64_t nBlockTreeDBCache,
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
#include <interfaces/chain.h>
|
|
10
10
|
#include <net.h>
|
|
11
11
|
#include <net_processing.h>
|
|
12
|
-
#include <policy/fees.h>
|
|
13
12
|
#include <scheduler.h>
|
|
14
13
|
#include <txmempool.h>
|
|
15
14
|
#include <validation.h>
|
|
@@ -10,21 +10,23 @@
|
|
|
10
10
|
#include <memory>
|
|
11
11
|
#include <vector>
|
|
12
12
|
|
|
13
|
+
#include <interfaces/init.h>
|
|
14
|
+
#include <interfaces/chain.h>
|
|
15
|
+
#include <interfaces/wallet.h>
|
|
16
|
+
|
|
13
17
|
class ArgsManager;
|
|
14
18
|
class BanMan;
|
|
15
19
|
class AddrMan;
|
|
16
|
-
class CBlockPolicyEstimator;
|
|
17
20
|
class CConnman;
|
|
18
21
|
class CScheduler;
|
|
19
22
|
class CTxMemPool;
|
|
20
23
|
class ChainstateManager;
|
|
21
24
|
class PeerManager;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} // namespace interfaces
|
|
25
|
+
|
|
26
|
+
using interfaces::Chain;
|
|
27
|
+
using interfaces::ChainClient;
|
|
28
|
+
using interfaces::Init;
|
|
29
|
+
using interfaces::WalletLoader;
|
|
28
30
|
|
|
29
31
|
namespace node {
|
|
30
32
|
//! NodeContext struct containing references to chain state and connection
|
|
@@ -43,7 +45,6 @@ struct NodeContext {
|
|
|
43
45
|
std::unique_ptr<AddrMan> addrman;
|
|
44
46
|
std::unique_ptr<CConnman> connman;
|
|
45
47
|
std::unique_ptr<CTxMemPool> mempool;
|
|
46
|
-
std::unique_ptr<CBlockPolicyEstimator> fee_estimator;
|
|
47
48
|
std::unique_ptr<PeerManager> peerman;
|
|
48
49
|
std::unique_ptr<ChainstateManager> chainman;
|
|
49
50
|
std::unique_ptr<BanMan> banman;
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
#include <banman.h>
|
|
7
7
|
#include <chain.h>
|
|
8
8
|
#include <chainparams.h>
|
|
9
|
-
#include <deploymentstatus.h>
|
|
10
9
|
#include <external_signer.h>
|
|
11
10
|
#include <init.h>
|
|
12
11
|
#include <interfaces/chain.h>
|
|
@@ -23,10 +22,7 @@
|
|
|
23
22
|
#include <node/context.h>
|
|
24
23
|
#include <node/transaction.h>
|
|
25
24
|
#include <node/ui_interface.h>
|
|
26
|
-
#include <policy/feerate.h>
|
|
27
|
-
#include <policy/fees.h>
|
|
28
25
|
#include <policy/policy.h>
|
|
29
|
-
#include <policy/rbf.h>
|
|
30
26
|
#include <policy/settings.h>
|
|
31
27
|
#include <primitives/block.h>
|
|
32
28
|
#include <primitives/transaction.h>
|
|
@@ -81,8 +77,8 @@ private:
|
|
|
81
77
|
class NodeImpl : public Node
|
|
82
78
|
{
|
|
83
79
|
private:
|
|
84
|
-
ChainstateManager& chainman() { return *Assert(m_context->chainman); }
|
|
85
80
|
public:
|
|
81
|
+
ChainstateManager& chainman() override { return *Assert(m_context->chainman); }
|
|
86
82
|
explicit NodeImpl(NodeContext& context) { setContext(&context); }
|
|
87
83
|
void initLogging() override { InitLogging(*Assert(m_context->args)); }
|
|
88
84
|
void initParameterInteraction() override { InitParameterInteraction(*Assert(m_context->args)); }
|
|
@@ -258,7 +254,6 @@ public:
|
|
|
258
254
|
}
|
|
259
255
|
}
|
|
260
256
|
bool getNetworkActive() override { return m_context->connman && m_context->connman->GetNetworkActive(); }
|
|
261
|
-
CFeeRate getDustRelayFee() override { return ::dustRelayFee; }
|
|
262
257
|
UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override
|
|
263
258
|
{
|
|
264
259
|
JSONRPCRequest req;
|
|
@@ -276,9 +271,9 @@ public:
|
|
|
276
271
|
LOCK(::cs_main);
|
|
277
272
|
return chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin);
|
|
278
273
|
}
|
|
279
|
-
TransactionError broadcastTransaction(CTransactionRef tx,
|
|
274
|
+
TransactionError broadcastTransaction(CTransactionRef tx, std::string& err_string) override
|
|
280
275
|
{
|
|
281
|
-
return BroadcastTransaction(*m_context, std::move(tx), err_string,
|
|
276
|
+
return BroadcastTransaction(*m_context, std::move(tx), err_string, /*relay=*/ true, /*wait_callback=*/ false);
|
|
282
277
|
}
|
|
283
278
|
WalletLoader& walletLoader() override
|
|
284
279
|
{
|
|
@@ -314,7 +309,9 @@ public:
|
|
|
314
309
|
}
|
|
315
310
|
std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) override
|
|
316
311
|
{
|
|
317
|
-
return MakeHandler(::uiInterface.NotifyAlertChanged_connect(fn)
|
|
312
|
+
return MakeHandler(::uiInterface.NotifyAlertChanged_connect([fn](const uint256 &hash, ChangeType status) {
|
|
313
|
+
fn(hash, status);
|
|
314
|
+
}));
|
|
318
315
|
}
|
|
319
316
|
std::unique_ptr<Handler> handleBannedListChanged(BannedListChangedFn fn) override
|
|
320
317
|
{
|
|
@@ -452,8 +449,8 @@ public:
|
|
|
452
449
|
class ChainImpl : public Chain
|
|
453
450
|
{
|
|
454
451
|
private:
|
|
455
|
-
ChainstateManager& chainman() { return *Assert(m_node.chainman); }
|
|
456
452
|
public:
|
|
453
|
+
ChainstateManager& chainman() override { return *Assert(m_node.chainman); }
|
|
457
454
|
explicit ChainImpl(NodeContext& node) : m_node(node) {}
|
|
458
455
|
std::optional<int> getHeight() override
|
|
459
456
|
{
|
|
@@ -550,7 +547,7 @@ public:
|
|
|
550
547
|
bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override
|
|
551
548
|
{
|
|
552
549
|
// hasBlocks returns true if all ancestors of block_hash in specified
|
|
553
|
-
// range have block data
|
|
550
|
+
// range have block data, false if any ancestors in
|
|
554
551
|
// specified range are missing data.
|
|
555
552
|
//
|
|
556
553
|
// For simplicity and robustness, min_height and max_height are only
|
|
@@ -566,12 +563,14 @@ public:
|
|
|
566
563
|
}
|
|
567
564
|
return false;
|
|
568
565
|
}
|
|
566
|
+
/*
|
|
569
567
|
RBFTransactionState isRBFOptIn(const CTransaction& tx) override
|
|
570
568
|
{
|
|
571
569
|
if (!m_node.mempool) return IsRBFOptInEmptyMempool(tx);
|
|
572
570
|
LOCK(m_node.mempool->cs);
|
|
573
571
|
return IsRBFOptIn(tx, *m_node.mempool);
|
|
574
572
|
}
|
|
573
|
+
*/
|
|
575
574
|
bool isInMempool(const uint256& txid) override
|
|
576
575
|
{
|
|
577
576
|
if (!m_node.mempool) return false;
|
|
@@ -586,11 +585,10 @@ public:
|
|
|
586
585
|
return it && (*it)->GetCountWithDescendants() > 1;
|
|
587
586
|
}
|
|
588
587
|
bool broadcastTransaction(const CTransactionRef& tx,
|
|
589
|
-
const CAmount& max_tx_fee,
|
|
590
588
|
bool relay,
|
|
591
589
|
std::string& err_string) override
|
|
592
590
|
{
|
|
593
|
-
const TransactionError err = BroadcastTransaction(m_node, tx, err_string,
|
|
591
|
+
const TransactionError err = BroadcastTransaction(m_node, tx, err_string, relay, /*wait_callback*/ false);
|
|
594
592
|
// Chain clients only care about failures to accept the tx to the mempool. Disregard non-mempool related failures.
|
|
595
593
|
// Note: this will need to be updated if BroadcastTransactions() is updated to return other non-mempool failures
|
|
596
594
|
// that Chain clients do not need to know about.
|
|
@@ -623,6 +621,7 @@ public:
|
|
|
623
621
|
entry, ancestors, limit_ancestor_count, limit_ancestor_size,
|
|
624
622
|
limit_descendant_count, limit_descendant_size, unused_error_string);
|
|
625
623
|
}
|
|
624
|
+
/*
|
|
626
625
|
CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override
|
|
627
626
|
{
|
|
628
627
|
if (!m_node.fee_estimator) return {};
|
|
@@ -641,11 +640,7 @@ public:
|
|
|
641
640
|
CFeeRate relayMinFee() override { return ::minRelayTxFee; }
|
|
642
641
|
CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
|
|
643
642
|
CFeeRate relayDustFee() override { return ::dustRelayFee; }
|
|
644
|
-
|
|
645
|
-
{
|
|
646
|
-
LOCK(cs_main);
|
|
647
|
-
return node::fHavePruned;
|
|
648
|
-
}
|
|
643
|
+
*/
|
|
649
644
|
bool isReadyToBroadcast() override { return !node::fImporting && !node::fReindex && !isInitialBlockDownload(); }
|
|
650
645
|
bool isInitialBlockDownload() override {
|
|
651
646
|
return chainman().ActiveChainstate().IsInitialBlockDownload();
|
|
@@ -13,34 +13,53 @@
|
|
|
13
13
|
#include <consensus/merkle.h>
|
|
14
14
|
#include <consensus/tx_verify.h>
|
|
15
15
|
#include <consensus/validation.h>
|
|
16
|
-
#include <deploymentstatus.h>
|
|
17
|
-
#include <policy/feerate.h>
|
|
18
16
|
#include <policy/policy.h>
|
|
19
17
|
#include <pow.h>
|
|
20
18
|
#include <primitives/transaction.h>
|
|
19
|
+
#include <rpc/blockchain.h>
|
|
21
20
|
#include <timedata.h>
|
|
21
|
+
#include <rpc/blockchain.h>
|
|
22
22
|
#include <util/moneystr.h>
|
|
23
23
|
#include <util/system.h>
|
|
24
|
+
#include <util/threadnames.h>
|
|
25
|
+
#include <util/translation.h>
|
|
24
26
|
#include <validation.h>
|
|
27
|
+
#include <kernel.h>
|
|
28
|
+
#include <net.h>
|
|
29
|
+
#include <interfaces/chain.h>
|
|
30
|
+
#include <node/context.h>
|
|
31
|
+
#include <node/ui_interface.h>
|
|
32
|
+
#include <util/thread.h>
|
|
33
|
+
#include <validation.h>
|
|
34
|
+
#include <wallet/wallet.h>
|
|
35
|
+
#include <wallet/coincontrol.h>
|
|
36
|
+
#include <warnings.h>
|
|
37
|
+
#include <wallet/spend.h>
|
|
38
|
+
#include <wallet/wallet.h>
|
|
25
39
|
|
|
26
40
|
#include <algorithm>
|
|
27
41
|
#include <utility>
|
|
28
42
|
|
|
43
|
+
#include <boost/thread.hpp>
|
|
44
|
+
|
|
45
|
+
using wallet::CWallet;
|
|
46
|
+
using wallet::COutput;
|
|
47
|
+
using wallet::CCoinControl;
|
|
48
|
+
using wallet::ReserveDestination;
|
|
49
|
+
|
|
50
|
+
int64_t nLastCoinStakeSearchInterval = 0;
|
|
51
|
+
std::thread m_minter_thread;
|
|
52
|
+
|
|
29
53
|
namespace node {
|
|
30
|
-
int64_t UpdateTime(CBlockHeader* pblock
|
|
54
|
+
int64_t UpdateTime(CBlockHeader* pblock)
|
|
31
55
|
{
|
|
32
56
|
int64_t nOldTime = pblock->nTime;
|
|
33
|
-
int64_t nNewTime = std::max(
|
|
57
|
+
int64_t nNewTime = std::max(pblock->GetBlockTime(), GetAdjustedTime());
|
|
34
58
|
|
|
35
59
|
if (nOldTime < nNewTime) {
|
|
36
60
|
pblock->nTime = nNewTime;
|
|
37
61
|
}
|
|
38
62
|
|
|
39
|
-
// Updating time can change work required on testnet:
|
|
40
|
-
if (consensusParams.fPowAllowMinDifficultyBlocks) {
|
|
41
|
-
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
63
|
return nNewTime - nOldTime;
|
|
45
64
|
}
|
|
46
65
|
|
|
@@ -58,7 +77,6 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
|
|
|
58
77
|
|
|
59
78
|
BlockAssembler::Options::Options()
|
|
60
79
|
{
|
|
61
|
-
blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
|
|
62
80
|
nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
|
|
63
81
|
}
|
|
64
82
|
|
|
@@ -67,7 +85,6 @@ BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempoo
|
|
|
67
85
|
m_mempool(mempool),
|
|
68
86
|
m_chainstate(chainstate)
|
|
69
87
|
{
|
|
70
|
-
blockMinFeeRate = options.blockMinFeeRate;
|
|
71
88
|
// Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity:
|
|
72
89
|
nBlockMaxWeight = std::max<size_t>(4000, std::min<size_t>(MAX_BLOCK_WEIGHT - 4000, options.nBlockMaxWeight));
|
|
73
90
|
}
|
|
@@ -78,12 +95,6 @@ static BlockAssembler::Options DefaultOptions()
|
|
|
78
95
|
// If -blockmaxweight is not given, limit to DEFAULT_BLOCK_MAX_WEIGHT
|
|
79
96
|
BlockAssembler::Options options;
|
|
80
97
|
options.nBlockMaxWeight = gArgs.GetIntArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
|
|
81
|
-
if (gArgs.IsArgSet("-blockmintxfee")) {
|
|
82
|
-
std::optional<CAmount> parsed = ParseMoney(gArgs.GetArg("-blockmintxfee", ""));
|
|
83
|
-
options.blockMinFeeRate = CFeeRate{parsed.value_or(DEFAULT_BLOCK_MIN_TX_FEE)};
|
|
84
|
-
} else {
|
|
85
|
-
options.blockMinFeeRate = CFeeRate{DEFAULT_BLOCK_MIN_TX_FEE};
|
|
86
|
-
}
|
|
87
98
|
return options;
|
|
88
99
|
}
|
|
89
100
|
|
|
@@ -104,7 +115,8 @@ void BlockAssembler::resetBlock()
|
|
|
104
115
|
nFees = 0;
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
|
|
118
|
+
// peercoin: if pwallet != NULL it will attempt to create coinstake
|
|
119
|
+
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, bool* pfPoSCancel, NodeContext* m_node)
|
|
108
120
|
{
|
|
109
121
|
int64_t nTimeStart = GetTimeMicros();
|
|
110
122
|
|
|
@@ -116,25 +128,67 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
|
|
116
128
|
return nullptr;
|
|
117
129
|
}
|
|
118
130
|
CBlock* const pblock = &pblocktemplate->block; // pointer for convenience
|
|
131
|
+
pblock->nTime = GetAdjustedTime();
|
|
132
|
+
|
|
133
|
+
LOCK2(cs_main, m_mempool.cs);
|
|
134
|
+
|
|
135
|
+
CBlockIndex* pindexPrev = m_node->chainman->ActiveChain().Tip();
|
|
136
|
+
assert(pindexPrev != nullptr);
|
|
137
|
+
nHeight = pindexPrev->nHeight + 1;
|
|
138
|
+
|
|
139
|
+
// Create coinbase transaction.
|
|
140
|
+
CMutableTransaction coinbaseTx;
|
|
141
|
+
coinbaseTx.vin.resize(1);
|
|
142
|
+
coinbaseTx.vin[0].prevout.SetNull();
|
|
143
|
+
coinbaseTx.vout.resize(1);
|
|
144
|
+
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
|
|
145
|
+
|
|
146
|
+
if (pwallet == nullptr) {
|
|
147
|
+
pblock->nBits = GetNextTargetRequired(pindexPrev, false, chainparams.GetConsensus());
|
|
148
|
+
coinbaseTx.vout[0].nValue = GetProofOfWorkReward(pblock->nBits, pblock->nTime);
|
|
149
|
+
}
|
|
119
150
|
|
|
120
151
|
// Add dummy coinbase tx as first transaction
|
|
121
152
|
pblock->vtx.emplace_back();
|
|
122
153
|
pblocktemplate->vTxFees.push_back(-1); // updated at end
|
|
123
154
|
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
|
|
124
155
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
156
|
+
// peercoin: if coinstake available add coinstake tx
|
|
157
|
+
static int64_t nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup
|
|
158
|
+
|
|
159
|
+
if (pwallet) // attemp to find a coinstake
|
|
160
|
+
{
|
|
161
|
+
*pfPoSCancel = true;
|
|
162
|
+
pblock->nBits = GetNextTargetRequired(pindexPrev, true, chainparams.GetConsensus());
|
|
163
|
+
CMutableTransaction txCoinStake;
|
|
164
|
+
int64_t nSearchTime = txCoinStake.nTime; // search to current time
|
|
165
|
+
if (nSearchTime > nLastCoinStakeSearchTime)
|
|
166
|
+
{
|
|
167
|
+
if (pwallet->CreateCoinStake(*m_node->chainman, pwallet, pblock->nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake))
|
|
168
|
+
{
|
|
169
|
+
if (txCoinStake.nTime >= std::max(pindexPrev->GetMedianTimePast()+1, pindexPrev->GetBlockTime() - (IsProtocolV09(pindexPrev->GetBlockTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9)))
|
|
170
|
+
{ // make sure coinstake would meet timestamp protocol
|
|
171
|
+
// as it would be the same as the block timestamp
|
|
172
|
+
coinbaseTx.vout[0].SetEmpty();
|
|
173
|
+
coinbaseTx.nTime = txCoinStake.nTime;
|
|
174
|
+
pblock->vtx.push_back(MakeTransactionRef(CTransaction(txCoinStake)));
|
|
175
|
+
*pfPoSCancel = false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime;
|
|
179
|
+
nLastCoinStakeSearchTime = nSearchTime;
|
|
180
|
+
}
|
|
181
|
+
if (*pfPoSCancel)
|
|
182
|
+
return nullptr; // peercoin: there is no point to continue if we failed to create coinstake
|
|
183
|
+
pblock->nFlags = CBlockIndex::BLOCK_PROOF_OF_STAKE;
|
|
184
|
+
}
|
|
129
185
|
|
|
130
|
-
pblock->nVersion = g_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
|
|
131
186
|
// -regtest only: allow overriding block.nVersion with
|
|
132
187
|
// -blockversion=N to test forking scenarios
|
|
133
188
|
if (chainparams.MineBlocksOnDemand()) {
|
|
134
189
|
pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion);
|
|
135
190
|
}
|
|
136
191
|
|
|
137
|
-
pblock->nTime = GetAdjustedTime();
|
|
138
192
|
m_lock_time_cutoff = pindexPrev->GetMedianTimePast();
|
|
139
193
|
|
|
140
194
|
// Decide whether to include witness transactions
|
|
@@ -146,40 +200,38 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
|
|
146
200
|
// not activated.
|
|
147
201
|
// TODO: replace this with a call to main to assess validity of a mempool
|
|
148
202
|
// transaction (which in most cases can be a no-op).
|
|
149
|
-
fIncludeWitness =
|
|
203
|
+
fIncludeWitness = IsBTC16BIPsEnabled(pindexPrev->nTime);
|
|
150
204
|
|
|
151
205
|
int nPackagesSelected = 0;
|
|
152
206
|
int nDescendantsUpdated = 0;
|
|
153
|
-
addPackageTxs(nPackagesSelected, nDescendantsUpdated);
|
|
207
|
+
addPackageTxs(nPackagesSelected, nDescendantsUpdated, pblock->nTime);
|
|
154
208
|
|
|
155
209
|
int64_t nTime1 = GetTimeMicros();
|
|
156
210
|
|
|
157
211
|
m_last_block_num_txs = nBlockTx;
|
|
158
212
|
m_last_block_weight = nBlockWeight;
|
|
159
213
|
|
|
160
|
-
// Create coinbase transaction.
|
|
161
|
-
CMutableTransaction coinbaseTx;
|
|
162
|
-
coinbaseTx.vin.resize(1);
|
|
163
|
-
coinbaseTx.vin[0].prevout.SetNull();
|
|
164
|
-
coinbaseTx.vout.resize(1);
|
|
165
|
-
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
|
|
166
|
-
coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
|
167
214
|
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
|
|
168
215
|
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
|
|
169
|
-
|
|
216
|
+
if (fIncludeWitness)
|
|
217
|
+
pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus());
|
|
170
218
|
pblocktemplate->vTxFees[0] = -nFees;
|
|
171
219
|
|
|
172
220
|
LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
|
|
173
221
|
|
|
174
222
|
// Fill in header
|
|
175
223
|
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
|
176
|
-
|
|
177
|
-
|
|
224
|
+
if (pblock->IsProofOfStake())
|
|
225
|
+
pblock->nTime = pblock->vtx[1]->nTime; //same as coinstake timestamp
|
|
226
|
+
pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime());
|
|
227
|
+
pblock->nTime = std::max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - (IsProtocolV09(pindexPrev->GetBlockTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9));
|
|
228
|
+
if (pblock->IsProofOfWork())
|
|
229
|
+
UpdateTime(pblock);
|
|
178
230
|
pblock->nNonce = 0;
|
|
179
231
|
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
|
|
180
232
|
|
|
181
233
|
BlockValidationState state;
|
|
182
|
-
if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
|
|
234
|
+
if (pwallet && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
|
|
183
235
|
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
|
|
184
236
|
}
|
|
185
237
|
int64_t nTime2 = GetTimeMicros();
|
|
@@ -217,7 +269,7 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost
|
|
|
217
269
|
// - transaction finality (locktime)
|
|
218
270
|
// - premature witness (in case segwit transactions are added to mempool before
|
|
219
271
|
// segwit activation)
|
|
220
|
-
bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package) const
|
|
272
|
+
bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package, uint32_t nTime) const
|
|
221
273
|
{
|
|
222
274
|
for (CTxMemPool::txiter it : package) {
|
|
223
275
|
if (!IsFinalTx(it->GetTx(), nHeight, m_lock_time_cutoff)) {
|
|
@@ -225,6 +277,10 @@ bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& packa
|
|
|
225
277
|
}
|
|
226
278
|
if (!fIncludeWitness && it->GetTx().HasWitness()) {
|
|
227
279
|
return false;
|
|
280
|
+
|
|
281
|
+
// peercoin: timestamp limit
|
|
282
|
+
if (it->GetTx().nTime > GetAdjustedTime() || (nTime && it->GetTx().nTime > nTime))
|
|
283
|
+
return false;
|
|
228
284
|
}
|
|
229
285
|
}
|
|
230
286
|
return true;
|
|
@@ -243,8 +299,8 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
|
|
|
243
299
|
|
|
244
300
|
bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
|
|
245
301
|
if (fPrintPriority) {
|
|
246
|
-
LogPrintf("fee
|
|
247
|
-
|
|
302
|
+
LogPrintf("fee %d satoshi txid %s\n",
|
|
303
|
+
iter->GetModifiedFee(),
|
|
248
304
|
iter->GetTx().GetHash().ToString());
|
|
249
305
|
}
|
|
250
306
|
}
|
|
@@ -317,7 +373,7 @@ void BlockAssembler::SortForBlock(const CTxMemPool::setEntries& package, std::ve
|
|
|
317
373
|
// Each time through the loop, we compare the best transaction in
|
|
318
374
|
// mapModifiedTxs with the next transaction in the mempool to decide what
|
|
319
375
|
// transaction package to work on next.
|
|
320
|
-
void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpdated)
|
|
376
|
+
void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpdated, uint32_t nTime)
|
|
321
377
|
{
|
|
322
378
|
AssertLockHeld(m_mempool.cs);
|
|
323
379
|
|
|
@@ -379,19 +435,12 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
|
|
|
379
435
|
assert(!inBlock.count(iter));
|
|
380
436
|
|
|
381
437
|
uint64_t packageSize = iter->GetSizeWithAncestors();
|
|
382
|
-
CAmount packageFees = iter->GetModFeesWithAncestors();
|
|
383
438
|
int64_t packageSigOpsCost = iter->GetSigOpCostWithAncestors();
|
|
384
439
|
if (fUsingModified) {
|
|
385
440
|
packageSize = modit->nSizeWithAncestors;
|
|
386
|
-
packageFees = modit->nModFeesWithAncestors;
|
|
387
441
|
packageSigOpsCost = modit->nSigOpCostWithAncestors;
|
|
388
442
|
}
|
|
389
443
|
|
|
390
|
-
if (packageFees < blockMinFeeRate.GetFee(packageSize)) {
|
|
391
|
-
// Everything else we might consider has a lower fee rate
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
444
|
if (!TestPackage(packageSize, packageSigOpsCost)) {
|
|
396
445
|
if (fUsingModified) {
|
|
397
446
|
// Since we always look at the best entry in mapModifiedTx,
|
|
@@ -420,7 +469,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
|
|
|
420
469
|
ancestors.insert(iter);
|
|
421
470
|
|
|
422
471
|
// Test if all tx's are Final
|
|
423
|
-
if (!TestPackageTransactions(ancestors)) {
|
|
472
|
+
if (!TestPackageTransactions(ancestors,nTime)) {
|
|
424
473
|
if (fUsingModified) {
|
|
425
474
|
mapModifiedTx.get<ancestor_score>().erase(modit);
|
|
426
475
|
failedTx.insert(iter);
|
|
@@ -465,4 +514,208 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
|
|
|
465
514
|
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
|
|
466
515
|
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
|
|
467
516
|
}
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
static bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainparams, NodeContext& m_node)
|
|
520
|
+
{
|
|
521
|
+
LogPrintf("%s\n", pblock->ToString());
|
|
522
|
+
LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0]->vout[0].nValue));
|
|
523
|
+
|
|
524
|
+
// Found a solution
|
|
525
|
+
{
|
|
526
|
+
LOCK(cs_main);
|
|
527
|
+
if (pblock->hashPrevBlock != m_node.chainman->ActiveChain().Tip()->GetBlockHash())
|
|
528
|
+
return error("PeercoinMiner: generated block is stale");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Process this block the same as if we had received it from another node
|
|
532
|
+
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
|
|
533
|
+
if (!m_node.chainman->ProcessNewBlock(Params(), shared_pblock, true, NULL))
|
|
534
|
+
return error("ProcessNewBlock, block not accepted");
|
|
535
|
+
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
void PoSMiner(std::shared_ptr<CWallet> pwallet, NodeContext& m_node)
|
|
540
|
+
{
|
|
541
|
+
CConnman* connman = m_node.connman.get();
|
|
542
|
+
LogPrintf("CPUMiner started for proof-of-stake\n");
|
|
543
|
+
util::ThreadRename("peercoin-stake-minter");
|
|
544
|
+
|
|
545
|
+
unsigned int nExtraNonce = 0;
|
|
546
|
+
|
|
547
|
+
OutputType output_type = pwallet->m_default_change_type ? *pwallet->m_default_change_type : pwallet->m_default_address_type;
|
|
548
|
+
ReserveDestination reservedest(pwallet.get(), output_type);
|
|
549
|
+
CTxDestination dest;
|
|
550
|
+
// Compute timeout for pos as sqrt(numUTXO)
|
|
551
|
+
unsigned int pos_timio;
|
|
552
|
+
{
|
|
553
|
+
LOCK2(pwallet->cs_wallet, cs_main);
|
|
554
|
+
bilingual_str dest_err;
|
|
555
|
+
if (!reservedest.GetReservedDestination(dest, true, dest_err))
|
|
556
|
+
throw std::runtime_error("Error: Keypool ran out, please call keypoolrefill first.");
|
|
557
|
+
|
|
558
|
+
std::vector<COutput> vCoins;
|
|
559
|
+
CCoinControl coincontrol;
|
|
560
|
+
AvailableCoins(*pwallet, vCoins, &coincontrol);
|
|
561
|
+
pos_timio = 500 + 30 * sqrt(vCoins.size());
|
|
562
|
+
LogPrintf("Set proof-of-stake timeout: %ums for %u UTXOs\n", pos_timio, vCoins.size());
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
std::string strMintMessage = _("Info: Minting suspended due to locked wallet.").translated;
|
|
566
|
+
std::string strMintSyncMessage = _("Info: Minting suspended while synchronizing wallet.").translated;
|
|
567
|
+
std::string strMintDisabledMessage = _("Info: Minting disabled by 'nominting' option.").translated;
|
|
568
|
+
std::string strMintBlockMessage = _("Info: Minting suspended due to block creation failure.").translated;
|
|
569
|
+
std::string strMintEmpty = "";
|
|
570
|
+
if (!gArgs.GetBoolArg("-minting", true) || !gArgs.GetBoolArg("-staking", true))
|
|
571
|
+
{
|
|
572
|
+
strMintWarning = strMintDisabledMessage;
|
|
573
|
+
LogPrintf("proof-of-stake minter disabled\n");
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
try {
|
|
578
|
+
bool fNeedToClear = false;
|
|
579
|
+
while (true) {
|
|
580
|
+
while (pwallet->IsLocked()) {
|
|
581
|
+
if (strMintWarning != strMintMessage) {
|
|
582
|
+
strMintWarning = strMintMessage;
|
|
583
|
+
uiInterface.NotifyAlertChanged(uint256(), CT_UPDATED);
|
|
584
|
+
}
|
|
585
|
+
fNeedToClear = true;
|
|
586
|
+
if (!connman->interruptNet.sleep_for(std::chrono::seconds(3)))
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (Params().MiningRequiresPeers()) {
|
|
591
|
+
// Busy-wait for the network to come online so we don't waste time mining
|
|
592
|
+
// on an obsolete chain. In regtest mode we expect to fly solo.
|
|
593
|
+
while(connman == nullptr || connman->GetNodeCount(ConnectionDirection::Both) == 0 || m_node.chainman->ActiveChainstate().IsInitialBlockDownload()) {
|
|
594
|
+
while(connman == nullptr) {UninterruptibleSleep(1s);}
|
|
595
|
+
if (!connman->interruptNet.sleep_for(std::chrono::seconds(10)))
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
while (GuessVerificationProgress(Params().TxData(), m_node.chainman->ActiveChain().Tip()) < 0.996)
|
|
601
|
+
{
|
|
602
|
+
LogPrintf("Minter thread sleeps while sync at %f\n", GuessVerificationProgress(Params().TxData(), m_node.chainman->ActiveChain().Tip()));
|
|
603
|
+
if (strMintWarning != strMintSyncMessage) {
|
|
604
|
+
strMintWarning = strMintSyncMessage;
|
|
605
|
+
uiInterface.NotifyAlertChanged(uint256(), CT_UPDATED);
|
|
606
|
+
}
|
|
607
|
+
fNeedToClear = true;
|
|
608
|
+
if (!connman->interruptNet.sleep_for(std::chrono::seconds(10)))
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (fNeedToClear) {
|
|
612
|
+
strMintWarning = strMintEmpty;
|
|
613
|
+
uiInterface.NotifyAlertChanged(uint256(), CT_UPDATED);
|
|
614
|
+
fNeedToClear = false;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
//
|
|
618
|
+
// Create new block
|
|
619
|
+
//
|
|
620
|
+
CBlockIndex* pindexPrev = m_node.chainman->ActiveChain().Tip();
|
|
621
|
+
bool fPoSCancel = false;
|
|
622
|
+
CScript scriptPubKey = GetScriptForDestination(dest);
|
|
623
|
+
CBlock *pblock;
|
|
624
|
+
std::unique_ptr<CBlockTemplate> pblocktemplate;
|
|
625
|
+
|
|
626
|
+
{
|
|
627
|
+
LOCK2(pwallet->cs_wallet, cs_main);
|
|
628
|
+
try {
|
|
629
|
+
pblocktemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, Params()).CreateNewBlock(scriptPubKey, pwallet.get(), &fPoSCancel, &m_node);
|
|
630
|
+
}
|
|
631
|
+
catch (const std::runtime_error &e)
|
|
632
|
+
{
|
|
633
|
+
LogPrintf("PeercoinMiner runtime error: %s\n", e.what());
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (!pblocktemplate.get())
|
|
639
|
+
{
|
|
640
|
+
if (fPoSCancel == true)
|
|
641
|
+
{
|
|
642
|
+
if (!connman->interruptNet.sleep_for(std::chrono::milliseconds(pos_timio)))
|
|
643
|
+
return;
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
strMintWarning = strMintBlockMessage;
|
|
647
|
+
uiInterface.NotifyAlertChanged(uint256(), CT_UPDATED);
|
|
648
|
+
LogPrintf("Error in PeercoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
|
|
649
|
+
if (!connman->interruptNet.sleep_for(std::chrono::seconds(10)))
|
|
650
|
+
return;
|
|
651
|
+
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
pblock = &pblocktemplate->block;
|
|
655
|
+
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
|
|
656
|
+
|
|
657
|
+
// peercoin: if proof-of-stake block found then process block
|
|
658
|
+
if (pblock->IsProofOfStake())
|
|
659
|
+
{
|
|
660
|
+
{
|
|
661
|
+
LOCK2(pwallet->cs_wallet, cs_main);
|
|
662
|
+
if (!SignBlock(*pblock, *pwallet))
|
|
663
|
+
{
|
|
664
|
+
LogPrintf("PoSMiner(): failed to sign PoS block");
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
LogPrintf("CPUMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString());
|
|
669
|
+
try {
|
|
670
|
+
ProcessBlockFound(pblock, Params(), m_node);
|
|
671
|
+
}
|
|
672
|
+
catch (const std::runtime_error &e)
|
|
673
|
+
{
|
|
674
|
+
LogPrintf("PeercoinMiner runtime error: %s\n", e.what());
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
reservedest.KeepDestination();
|
|
678
|
+
// Rest for ~3 minutes after successful block to preserve close quick
|
|
679
|
+
if (!connman->interruptNet.sleep_for(std::chrono::seconds(60 + GetRand(4))))
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (!connman->interruptNet.sleep_for(std::chrono::milliseconds(pos_timio)))
|
|
683
|
+
return;
|
|
684
|
+
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
catch (::boost::thread_interrupted)
|
|
689
|
+
{
|
|
690
|
+
LogPrintf("PeercoinMiner terminated\n");
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
catch (const std::runtime_error &e)
|
|
694
|
+
{
|
|
695
|
+
LogPrintf("PeercoinMiner runtime error: %s\n", e.what());
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// peercoin: stake minter thread
|
|
701
|
+
void static ThreadStakeMinter(std::shared_ptr<CWallet> pwallet, NodeContext& m_node)
|
|
702
|
+
{
|
|
703
|
+
LogPrintf("ThreadStakeMinter started\n");
|
|
704
|
+
try
|
|
705
|
+
{
|
|
706
|
+
PoSMiner(pwallet, m_node);
|
|
707
|
+
}
|
|
708
|
+
catch (std::exception& e) {
|
|
709
|
+
PrintExceptionContinue(&e, "ThreadStakeMinter()");
|
|
710
|
+
} catch (...) {
|
|
711
|
+
PrintExceptionContinue(NULL, "ThreadStakeMinter()");
|
|
712
|
+
}
|
|
713
|
+
LogPrintf("ThreadStakeMinter exiting\n");
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// peercoin: stake minter
|
|
717
|
+
void MintStake(std::shared_ptr<CWallet> pwallet, NodeContext& m_node)
|
|
718
|
+
{
|
|
719
|
+
m_minter_thread = std::thread([&] { util::TraceThread("minter", [&] { ThreadStakeMinter(pwallet, m_node); }); });
|
|
720
|
+
}
|
|
468
721
|
} // namespace node
|
|
@@ -8,15 +8,18 @@
|
|
|
8
8
|
|
|
9
9
|
#include <primitives/block.h>
|
|
10
10
|
#include <txmempool.h>
|
|
11
|
-
|
|
11
|
+
#include <node/context.h>
|
|
12
12
|
#include <memory>
|
|
13
13
|
#include <optional>
|
|
14
14
|
#include <stdint.h>
|
|
15
|
+
#include <wallet/wallet.h>
|
|
15
16
|
|
|
16
17
|
#include <boost/multi_index/ordered_index.hpp>
|
|
17
18
|
#include <boost/multi_index_container.hpp>
|
|
18
19
|
|
|
20
|
+
extern int64_t nLastCoinStakeSearchInterval;
|
|
19
21
|
class ChainstateManager;
|
|
22
|
+
|
|
20
23
|
class CBlockIndex;
|
|
21
24
|
class CChainParams;
|
|
22
25
|
class CScript;
|
|
@@ -134,7 +137,6 @@ private:
|
|
|
134
137
|
// Configuration parameters for the block size
|
|
135
138
|
bool fIncludeWitness;
|
|
136
139
|
unsigned int nBlockMaxWeight;
|
|
137
|
-
CFeeRate blockMinFeeRate;
|
|
138
140
|
|
|
139
141
|
// Information on the current status of the block
|
|
140
142
|
uint64_t nBlockWeight;
|
|
@@ -155,14 +157,14 @@ public:
|
|
|
155
157
|
struct Options {
|
|
156
158
|
Options();
|
|
157
159
|
size_t nBlockMaxWeight;
|
|
158
|
-
CFeeRate blockMinFeeRate;
|
|
159
160
|
};
|
|
160
161
|
|
|
161
162
|
explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params);
|
|
162
163
|
explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params, const Options& options);
|
|
163
164
|
|
|
164
165
|
/** Construct a new block template with coinbase to scriptPubKeyIn */
|
|
165
|
-
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
|
|
166
|
+
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet=nullptr, bool* pfPoSCancel=nullptr, NodeContext* m_node=nullptr);
|
|
167
|
+
//std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
|
|
166
168
|
|
|
167
169
|
inline static std::optional<int64_t> m_last_block_num_txs{};
|
|
168
170
|
inline static std::optional<int64_t> m_last_block_weight{};
|
|
@@ -178,7 +180,7 @@ private:
|
|
|
178
180
|
/** Add transactions based on feerate including unconfirmed ancestors
|
|
179
181
|
* Increments nPackagesSelected / nDescendantsUpdated with corresponding
|
|
180
182
|
* statistics from the package selection (for logging statistics). */
|
|
181
|
-
void addPackageTxs(int& nPackagesSelected, int& nDescendantsUpdated) EXCLUSIVE_LOCKS_REQUIRED(m_mempool.cs);
|
|
183
|
+
void addPackageTxs(int& nPackagesSelected, int& nDescendantsUpdated, uint32_t nTime) EXCLUSIVE_LOCKS_REQUIRED(m_mempool.cs);
|
|
182
184
|
|
|
183
185
|
// helper functions for addPackageTxs()
|
|
184
186
|
/** Remove confirmed (inBlock) entries from given set */
|
|
@@ -189,7 +191,7 @@ private:
|
|
|
189
191
|
* locktime, premature-witness, serialized size (if necessary)
|
|
190
192
|
* These checks should always succeed, and they're here
|
|
191
193
|
* only as an extra check in case of suboptimal node configuration */
|
|
192
|
-
bool TestPackageTransactions(const CTxMemPool::setEntries& package) const;
|
|
194
|
+
bool TestPackageTransactions(const CTxMemPool::setEntries& package, uint32_t nTime) const;
|
|
193
195
|
/** Return true if given transaction from mapTx has already been evaluated,
|
|
194
196
|
* or if the transaction's cached data in mapTx is incorrect. */
|
|
195
197
|
bool SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set& mapModifiedTx, CTxMemPool::setEntries& failedTx) EXCLUSIVE_LOCKS_REQUIRED(m_mempool.cs);
|
|
@@ -203,7 +205,13 @@ private:
|
|
|
203
205
|
|
|
204
206
|
/** Modify the extranonce in a block */
|
|
205
207
|
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
|
|
206
|
-
int64_t UpdateTime(CBlockHeader* pblock
|
|
208
|
+
int64_t UpdateTime(CBlockHeader* pblock);
|
|
209
|
+
|
|
210
|
+
namespace boost {
|
|
211
|
+
class thread_group;
|
|
212
|
+
} // namespace boost
|
|
213
|
+
|
|
214
|
+
void MintStake(std::shared_ptr<CWallet> pwallet, NodeContext& m_node);
|
|
207
215
|
|
|
208
216
|
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */
|
|
209
217
|
void RegenerateCommitments(CBlock& block, ChainstateManager& chainman);
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
#include <coins.h>
|
|
6
6
|
#include <consensus/amount.h>
|
|
7
7
|
#include <consensus/tx_verify.h>
|
|
8
|
+
#include <kernel.h>
|
|
8
9
|
#include <node/psbt.h>
|
|
9
10
|
#include <policy/policy.h>
|
|
10
11
|
#include <policy/settings.h>
|
|
12
|
+
#include <timedata.h>
|
|
11
13
|
#include <tinyformat.h>
|
|
12
14
|
|
|
13
15
|
#include <numeric>
|
|
@@ -131,7 +133,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
|
|
|
131
133
|
mtx.vin[i].scriptSig = input.final_script_sig;
|
|
132
134
|
mtx.vin[i].scriptWitness = input.final_script_witness;
|
|
133
135
|
newcoin.nHeight = 1;
|
|
134
|
-
view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
|
|
136
|
+
view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true, true);
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
|
|
@@ -139,9 +141,6 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
|
|
|
139
141
|
CTransaction ctx = CTransaction(mtx);
|
|
140
142
|
size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
|
|
141
143
|
result.estimated_vsize = size;
|
|
142
|
-
// Estimate fee rate
|
|
143
|
-
CFeeRate feerate(fee, size);
|
|
144
|
-
result.estimated_feerate = feerate;
|
|
145
144
|
}
|
|
146
145
|
|
|
147
146
|
}
|
|
@@ -29,7 +29,6 @@ struct PSBTInputAnalysis {
|
|
|
29
29
|
*/
|
|
30
30
|
struct PSBTAnalysis {
|
|
31
31
|
std::optional<size_t> estimated_vsize; //!< Estimated weight of the transaction
|
|
32
|
-
std::optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction
|
|
33
32
|
std::optional<CAmount> fee; //!< Amount of fee being paid by the transaction
|
|
34
33
|
std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction
|
|
35
34
|
PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next
|
|
@@ -38,7 +37,6 @@ struct PSBTAnalysis {
|
|
|
38
37
|
void SetInvalid(std::string err_msg)
|
|
39
38
|
{
|
|
40
39
|
estimated_vsize = std::nullopt;
|
|
41
|
-
estimated_feerate = std::nullopt;
|
|
42
40
|
fee = std::nullopt;
|
|
43
41
|
inputs.clear();
|
|
44
42
|
next = PSBTRole::CREATOR;
|
|
@@ -30,7 +30,7 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string,
|
|
33
|
+
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, bool relay, bool wait_callback)
|
|
34
34
|
{
|
|
35
35
|
// BroadcastTransaction can be called by either sendrawtransaction RPC or the wallet.
|
|
36
36
|
// chainman, mempool and peerman are initialized before the RPC server and wallet are started
|
|
@@ -68,16 +68,6 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
|
|
|
68
68
|
wtxid = mempool_tx->GetWitnessHash();
|
|
69
69
|
} else {
|
|
70
70
|
// Transaction is not already in the mempool.
|
|
71
|
-
if (max_tx_fee > 0) {
|
|
72
|
-
// First, call ATMP with test_accept and check the fee. If ATMP
|
|
73
|
-
// fails here, return error immediately.
|
|
74
|
-
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);
|
|
75
|
-
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
|
76
|
-
return HandleATMPError(result.m_state, err_string);
|
|
77
|
-
} else if (result.m_base_fees.value() > max_tx_fee) {
|
|
78
|
-
return TransactionError::MAX_FEE_EXCEEDED;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
71
|
// Try to submit the transaction to the mempool.
|
|
82
72
|
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ false);
|
|
83
73
|
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
#define BITCOIN_NODE_TRANSACTION_H
|
|
7
7
|
|
|
8
8
|
#include <attributes.h>
|
|
9
|
-
#include <policy/feerate.h>
|
|
10
9
|
#include <primitives/transaction.h>
|
|
11
10
|
#include <util/error.h>
|
|
12
11
|
|
|
@@ -19,13 +18,6 @@ struct Params;
|
|
|
19
18
|
namespace node {
|
|
20
19
|
struct NodeContext;
|
|
21
20
|
|
|
22
|
-
/** Maximum fee rate for sendrawtransaction and testmempoolaccept RPC calls.
|
|
23
|
-
* Also used by the GUI when broadcasting a completed PSBT.
|
|
24
|
-
* By default, a transaction with a fee rate higher than this will be rejected
|
|
25
|
-
* by these RPCs and the GUI. This can be overridden with the maxfeerate argument.
|
|
26
|
-
*/
|
|
27
|
-
static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10};
|
|
28
|
-
|
|
29
21
|
/**
|
|
30
22
|
* Submit a transaction to the mempool and (optionally) relay it to all P2P peers.
|
|
31
23
|
*
|
|
@@ -38,12 +30,11 @@ static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10};
|
|
|
38
30
|
* @param[in] node reference to node context
|
|
39
31
|
* @param[in] tx the transaction to broadcast
|
|
40
32
|
* @param[out] err_string reference to std::string to fill with error string if available
|
|
41
|
-
* @param[in] max_tx_fee reject txs with fees higher than this (if 0, accept any fee)
|
|
42
33
|
* @param[in] relay flag if both mempool insertion and p2p relay are requested
|
|
43
34
|
* @param[in] wait_callback wait until callbacks have been processed to avoid stale result due to a sequentially RPC.
|
|
44
35
|
* return error
|
|
45
36
|
*/
|
|
46
|
-
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string,
|
|
37
|
+
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, bool relay, bool wait_callback);
|
|
47
38
|
|
|
48
39
|
/**
|
|
49
40
|
* Return transaction with a given hash.
|
|
@@ -50,7 +50,7 @@ void CClientUIInterface::InitMessage(const std::string& message) { return g_ui_s
|
|
|
50
50
|
void CClientUIInterface::InitWallet() { return g_ui_signals.InitWallet(); }
|
|
51
51
|
void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { return g_ui_signals.NotifyNumConnectionsChanged(newNumConnections); }
|
|
52
52
|
void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); }
|
|
53
|
-
void CClientUIInterface::NotifyAlertChanged() { return g_ui_signals.NotifyAlertChanged(); }
|
|
53
|
+
void CClientUIInterface::NotifyAlertChanged(const uint256 &hash, ChangeType status) { return g_ui_signals.NotifyAlertChanged(hash, status); }
|
|
54
54
|
void CClientUIInterface::ShowProgress(const std::string& title, int nProgress, bool resume_possible) { return g_ui_signals.ShowProgress(title, nProgress, resume_possible); }
|
|
55
55
|
void CClientUIInterface::NotifyBlockTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyBlockTip(s, i); }
|
|
56
56
|
void CClientUIInterface::NotifyHeaderTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyHeaderTip(s, i); }
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
#include <functional>
|
|
10
10
|
#include <memory>
|
|
11
11
|
#include <string>
|
|
12
|
+
#include <util/ui_change_type.h>
|
|
12
13
|
|
|
13
14
|
class CBlockIndex;
|
|
15
|
+
class uint256;
|
|
14
16
|
enum class SynchronizationState;
|
|
15
17
|
struct bilingual_str;
|
|
16
18
|
|
|
@@ -93,7 +95,7 @@ public:
|
|
|
93
95
|
/**
|
|
94
96
|
* Status bar alerts changed.
|
|
95
97
|
*/
|
|
96
|
-
ADD_SIGNALS_DECL_WRAPPER(NotifyAlertChanged, void, );
|
|
98
|
+
ADD_SIGNALS_DECL_WRAPPER(NotifyAlertChanged, void, const uint256 &hash, ChangeType status);
|
|
97
99
|
|
|
98
100
|
/**
|
|
99
101
|
* Show progress e.g. for verifychain.
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
-
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
|
3
|
-
// Distributed under the MIT software license, see the accompanying
|
|
4
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
-
|
|
6
|
-
#include <policy/feerate.h>
|
|
7
|
-
|
|
8
|
-
#include <tinyformat.h>
|
|
9
|
-
|
|
10
|
-
#include <cmath>
|
|
11
|
-
|
|
12
|
-
CFeeRate::CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes)
|
|
13
|
-
{
|
|
14
|
-
const int64_t nSize{num_bytes};
|
|
15
|
-
|
|
16
|
-
if (nSize > 0) {
|
|
17
|
-
nSatoshisPerK = nFeePaid * 1000 / nSize;
|
|
18
|
-
} else {
|
|
19
|
-
nSatoshisPerK = 0;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
CAmount CFeeRate::GetFee(uint32_t num_bytes) const
|
|
24
|
-
{
|
|
25
|
-
const int64_t nSize{num_bytes};
|
|
26
|
-
|
|
27
|
-
// Be explicit that we're converting from a double to int64_t (CAmount) here.
|
|
28
|
-
// We've previously had issues with the silent double->int64_t conversion.
|
|
29
|
-
CAmount nFee{static_cast<CAmount>(std::ceil(nSatoshisPerK * nSize / 1000.0))};
|
|
30
|
-
|
|
31
|
-
if (nFee == 0 && nSize != 0) {
|
|
32
|
-
if (nSatoshisPerK > 0) nFee = CAmount(1);
|
|
33
|
-
if (nSatoshisPerK < 0) nFee = CAmount(-1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return nFee;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
std::string CFeeRate::ToString(const FeeEstimateMode& fee_estimate_mode) const
|
|
40
|
-
{
|
|
41
|
-
switch (fee_estimate_mode) {
|
|
42
|
-
case FeeEstimateMode::SAT_VB: return strprintf("%d.%03d %s/vB", nSatoshisPerK / 1000, nSatoshisPerK % 1000, CURRENCY_ATOM);
|
|
43
|
-
default: return strprintf("%d.%08d %s/kvB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
-
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
|
3
|
-
// Distributed under the MIT software license, see the accompanying
|
|
4
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
-
|
|
6
|
-
#ifndef BITCOIN_POLICY_FEERATE_H
|
|
7
|
-
#define BITCOIN_POLICY_FEERATE_H
|
|
8
|
-
|
|
9
|
-
#include <consensus/amount.h>
|
|
10
|
-
#include <serialize.h>
|
|
11
|
-
|
|
12
|
-
#include <string>
|
|
13
|
-
|
|
14
|
-
const std::string CURRENCY_UNIT = "BTC"; // One formatted unit
|
|
15
|
-
const std::string CURRENCY_ATOM = "sat"; // One indivisible minimum value unit
|
|
16
|
-
|
|
17
|
-
/* Used to determine type of fee estimation requested */
|
|
18
|
-
enum class FeeEstimateMode {
|
|
19
|
-
UNSET, //!< Use default settings based on other criteria
|
|
20
|
-
ECONOMICAL, //!< Force estimateSmartFee to use non-conservative estimates
|
|
21
|
-
CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
|
|
22
|
-
BTC_KVB, //!< Use BTC/kvB fee rate unit
|
|
23
|
-
SAT_VB, //!< Use sat/vB fee rate unit
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Fee rate in satoshis per kilovirtualbyte: CAmount / kvB
|
|
28
|
-
*/
|
|
29
|
-
class CFeeRate
|
|
30
|
-
{
|
|
31
|
-
private:
|
|
32
|
-
/** Fee rate in sat/kvB (satoshis per 1000 virtualbytes) */
|
|
33
|
-
CAmount nSatoshisPerK;
|
|
34
|
-
|
|
35
|
-
public:
|
|
36
|
-
/** Fee rate of 0 satoshis per kvB */
|
|
37
|
-
CFeeRate() : nSatoshisPerK(0) { }
|
|
38
|
-
template<typename I>
|
|
39
|
-
explicit CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) {
|
|
40
|
-
// We've previously had bugs creep in from silent double->int conversion...
|
|
41
|
-
static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Construct a fee rate from a fee in satoshis and a vsize in vB.
|
|
46
|
-
*
|
|
47
|
-
* param@[in] nFeePaid The fee paid by a transaction, in satoshis
|
|
48
|
-
* param@[in] num_bytes The vsize of a transaction, in vbytes
|
|
49
|
-
*/
|
|
50
|
-
CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes);
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Return the fee in satoshis for the given vsize in vbytes.
|
|
54
|
-
* If the calculated fee would have fractional satoshis, then the
|
|
55
|
-
* returned fee will always be rounded up to the nearest satoshi.
|
|
56
|
-
*/
|
|
57
|
-
CAmount GetFee(uint32_t num_bytes) const;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Return the fee in satoshis for a vsize of 1000 vbytes
|
|
61
|
-
*/
|
|
62
|
-
CAmount GetFeePerK() const { return GetFee(1000); }
|
|
63
|
-
friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; }
|
|
64
|
-
friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; }
|
|
65
|
-
friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; }
|
|
66
|
-
friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; }
|
|
67
|
-
friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; }
|
|
68
|
-
friend bool operator!=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK != b.nSatoshisPerK; }
|
|
69
|
-
CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
|
|
70
|
-
std::string ToString(const FeeEstimateMode& fee_estimate_mode = FeeEstimateMode::BTC_KVB) const;
|
|
71
|
-
|
|
72
|
-
SERIALIZE_METHODS(CFeeRate, obj) { READWRITE(obj.nSatoshisPerK); }
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
#endif // BITCOIN_POLICY_FEERATE_H
|
|
@@ -1,1017 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
-
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
|
3
|
-
// Distributed under the MIT software license, see the accompanying
|
|
4
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
-
|
|
6
|
-
#include <policy/fees.h>
|
|
7
|
-
|
|
8
|
-
#include <clientversion.h>
|
|
9
|
-
#include <fs.h>
|
|
10
|
-
#include <logging.h>
|
|
11
|
-
#include <streams.h>
|
|
12
|
-
#include <txmempool.h>
|
|
13
|
-
#include <util/serfloat.h>
|
|
14
|
-
#include <util/system.h>
|
|
15
|
-
|
|
16
|
-
static const char* FEE_ESTIMATES_FILENAME = "fee_estimates.dat";
|
|
17
|
-
|
|
18
|
-
static constexpr double INF_FEERATE = 1e99;
|
|
19
|
-
|
|
20
|
-
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon)
|
|
21
|
-
{
|
|
22
|
-
switch (horizon) {
|
|
23
|
-
case FeeEstimateHorizon::SHORT_HALFLIFE: return "short";
|
|
24
|
-
case FeeEstimateHorizon::MED_HALFLIFE: return "medium";
|
|
25
|
-
case FeeEstimateHorizon::LONG_HALFLIFE: return "long";
|
|
26
|
-
} // no default case, so the compiler can warn about missing cases
|
|
27
|
-
assert(false);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
namespace {
|
|
31
|
-
|
|
32
|
-
struct EncodedDoubleFormatter
|
|
33
|
-
{
|
|
34
|
-
template<typename Stream> void Ser(Stream &s, double v)
|
|
35
|
-
{
|
|
36
|
-
s << EncodeDouble(v);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
template<typename Stream> void Unser(Stream& s, double& v)
|
|
40
|
-
{
|
|
41
|
-
uint64_t encoded;
|
|
42
|
-
s >> encoded;
|
|
43
|
-
v = DecodeDouble(encoded);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
} // namespace
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* We will instantiate an instance of this class to track transactions that were
|
|
51
|
-
* included in a block. We will lump transactions into a bucket according to their
|
|
52
|
-
* approximate feerate and then track how long it took for those txs to be included in a block
|
|
53
|
-
*
|
|
54
|
-
* The tracking of unconfirmed (mempool) transactions is completely independent of the
|
|
55
|
-
* historical tracking of transactions that have been confirmed in a block.
|
|
56
|
-
*/
|
|
57
|
-
class TxConfirmStats
|
|
58
|
-
{
|
|
59
|
-
private:
|
|
60
|
-
//Define the buckets we will group transactions into
|
|
61
|
-
const std::vector<double>& buckets; // The upper-bound of the range for the bucket (inclusive)
|
|
62
|
-
const std::map<double, unsigned int>& bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
|
|
63
|
-
|
|
64
|
-
// For each bucket X:
|
|
65
|
-
// Count the total # of txs in each bucket
|
|
66
|
-
// Track the historical moving average of this total over blocks
|
|
67
|
-
std::vector<double> txCtAvg;
|
|
68
|
-
|
|
69
|
-
// Count the total # of txs confirmed within Y blocks in each bucket
|
|
70
|
-
// Track the historical moving average of these totals over blocks
|
|
71
|
-
std::vector<std::vector<double>> confAvg; // confAvg[Y][X]
|
|
72
|
-
|
|
73
|
-
// Track moving avg of txs which have been evicted from the mempool
|
|
74
|
-
// after failing to be confirmed within Y blocks
|
|
75
|
-
std::vector<std::vector<double>> failAvg; // failAvg[Y][X]
|
|
76
|
-
|
|
77
|
-
// Sum the total feerate of all tx's in each bucket
|
|
78
|
-
// Track the historical moving average of this total over blocks
|
|
79
|
-
std::vector<double> m_feerate_avg;
|
|
80
|
-
|
|
81
|
-
// Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
|
|
82
|
-
// Combine the total value with the tx counts to calculate the avg feerate per bucket
|
|
83
|
-
|
|
84
|
-
double decay;
|
|
85
|
-
|
|
86
|
-
// Resolution (# of blocks) with which confirmations are tracked
|
|
87
|
-
unsigned int scale;
|
|
88
|
-
|
|
89
|
-
// Mempool counts of outstanding transactions
|
|
90
|
-
// For each bucket X, track the number of transactions in the mempool
|
|
91
|
-
// that are unconfirmed for each possible confirmation value Y
|
|
92
|
-
std::vector<std::vector<int> > unconfTxs; //unconfTxs[Y][X]
|
|
93
|
-
// transactions still unconfirmed after GetMaxConfirms for each bucket
|
|
94
|
-
std::vector<int> oldUnconfTxs;
|
|
95
|
-
|
|
96
|
-
void resizeInMemoryCounters(size_t newbuckets);
|
|
97
|
-
|
|
98
|
-
public:
|
|
99
|
-
/**
|
|
100
|
-
* Create new TxConfirmStats. This is called by BlockPolicyEstimator's
|
|
101
|
-
* constructor with default values.
|
|
102
|
-
* @param defaultBuckets contains the upper limits for the bucket boundaries
|
|
103
|
-
* @param maxPeriods max number of periods to track
|
|
104
|
-
* @param decay how much to decay the historical moving average per block
|
|
105
|
-
*/
|
|
106
|
-
TxConfirmStats(const std::vector<double>& defaultBuckets, const std::map<double, unsigned int>& defaultBucketMap,
|
|
107
|
-
unsigned int maxPeriods, double decay, unsigned int scale);
|
|
108
|
-
|
|
109
|
-
/** Roll the circular buffer for unconfirmed txs*/
|
|
110
|
-
void ClearCurrent(unsigned int nBlockHeight);
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Record a new transaction data point in the current block stats
|
|
114
|
-
* @param blocksToConfirm the number of blocks it took this transaction to confirm
|
|
115
|
-
* @param val the feerate of the transaction
|
|
116
|
-
* @warning blocksToConfirm is 1-based and has to be >= 1
|
|
117
|
-
*/
|
|
118
|
-
void Record(int blocksToConfirm, double val);
|
|
119
|
-
|
|
120
|
-
/** Record a new transaction entering the mempool*/
|
|
121
|
-
unsigned int NewTx(unsigned int nBlockHeight, double val);
|
|
122
|
-
|
|
123
|
-
/** Remove a transaction from mempool tracking stats*/
|
|
124
|
-
void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight,
|
|
125
|
-
unsigned int bucketIndex, bool inBlock);
|
|
126
|
-
|
|
127
|
-
/** Update our estimates by decaying our historical moving average and updating
|
|
128
|
-
with the data gathered from the current block */
|
|
129
|
-
void UpdateMovingAverages();
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Calculate a feerate estimate. Find the lowest value bucket (or range of buckets
|
|
133
|
-
* to make sure we have enough data points) whose transactions still have sufficient likelihood
|
|
134
|
-
* of being confirmed within the target number of confirmations
|
|
135
|
-
* @param confTarget target number of confirmations
|
|
136
|
-
* @param sufficientTxVal required average number of transactions per block in a bucket range
|
|
137
|
-
* @param minSuccess the success probability we require
|
|
138
|
-
* @param nBlockHeight the current block height
|
|
139
|
-
*/
|
|
140
|
-
double EstimateMedianVal(int confTarget, double sufficientTxVal,
|
|
141
|
-
double minSuccess, unsigned int nBlockHeight,
|
|
142
|
-
EstimationResult *result = nullptr) const;
|
|
143
|
-
|
|
144
|
-
/** Return the max number of confirms we're tracking */
|
|
145
|
-
unsigned int GetMaxConfirms() const { return scale * confAvg.size(); }
|
|
146
|
-
|
|
147
|
-
/** Write state of estimation data to a file*/
|
|
148
|
-
void Write(CAutoFile& fileout) const;
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Read saved state of estimation data from a file and replace all internal data structures and
|
|
152
|
-
* variables with this state.
|
|
153
|
-
*/
|
|
154
|
-
void Read(CAutoFile& filein, int nFileVersion, size_t numBuckets);
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
|
|
159
|
-
const std::map<double, unsigned int>& defaultBucketMap,
|
|
160
|
-
unsigned int maxPeriods, double _decay, unsigned int _scale)
|
|
161
|
-
: buckets(defaultBuckets), bucketMap(defaultBucketMap), decay(_decay), scale(_scale)
|
|
162
|
-
{
|
|
163
|
-
assert(_scale != 0 && "_scale must be non-zero");
|
|
164
|
-
confAvg.resize(maxPeriods);
|
|
165
|
-
failAvg.resize(maxPeriods);
|
|
166
|
-
for (unsigned int i = 0; i < maxPeriods; i++) {
|
|
167
|
-
confAvg[i].resize(buckets.size());
|
|
168
|
-
failAvg[i].resize(buckets.size());
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
txCtAvg.resize(buckets.size());
|
|
172
|
-
m_feerate_avg.resize(buckets.size());
|
|
173
|
-
|
|
174
|
-
resizeInMemoryCounters(buckets.size());
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
void TxConfirmStats::resizeInMemoryCounters(size_t newbuckets) {
|
|
178
|
-
// newbuckets must be passed in because the buckets referred to during Read have not been updated yet.
|
|
179
|
-
unconfTxs.resize(GetMaxConfirms());
|
|
180
|
-
for (unsigned int i = 0; i < unconfTxs.size(); i++) {
|
|
181
|
-
unconfTxs[i].resize(newbuckets);
|
|
182
|
-
}
|
|
183
|
-
oldUnconfTxs.resize(newbuckets);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Roll the unconfirmed txs circular buffer
|
|
187
|
-
void TxConfirmStats::ClearCurrent(unsigned int nBlockHeight)
|
|
188
|
-
{
|
|
189
|
-
for (unsigned int j = 0; j < buckets.size(); j++) {
|
|
190
|
-
oldUnconfTxs[j] += unconfTxs[nBlockHeight % unconfTxs.size()][j];
|
|
191
|
-
unconfTxs[nBlockHeight%unconfTxs.size()][j] = 0;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
void TxConfirmStats::Record(int blocksToConfirm, double feerate)
|
|
197
|
-
{
|
|
198
|
-
// blocksToConfirm is 1-based
|
|
199
|
-
if (blocksToConfirm < 1)
|
|
200
|
-
return;
|
|
201
|
-
int periodsToConfirm = (blocksToConfirm + scale - 1) / scale;
|
|
202
|
-
unsigned int bucketindex = bucketMap.lower_bound(feerate)->second;
|
|
203
|
-
for (size_t i = periodsToConfirm; i <= confAvg.size(); i++) {
|
|
204
|
-
confAvg[i - 1][bucketindex]++;
|
|
205
|
-
}
|
|
206
|
-
txCtAvg[bucketindex]++;
|
|
207
|
-
m_feerate_avg[bucketindex] += feerate;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
void TxConfirmStats::UpdateMovingAverages()
|
|
211
|
-
{
|
|
212
|
-
assert(confAvg.size() == failAvg.size());
|
|
213
|
-
for (unsigned int j = 0; j < buckets.size(); j++) {
|
|
214
|
-
for (unsigned int i = 0; i < confAvg.size(); i++) {
|
|
215
|
-
confAvg[i][j] *= decay;
|
|
216
|
-
failAvg[i][j] *= decay;
|
|
217
|
-
}
|
|
218
|
-
m_feerate_avg[j] *= decay;
|
|
219
|
-
txCtAvg[j] *= decay;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// returns -1 on error conditions
|
|
224
|
-
double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
|
|
225
|
-
double successBreakPoint, unsigned int nBlockHeight,
|
|
226
|
-
EstimationResult *result) const
|
|
227
|
-
{
|
|
228
|
-
// Counters for a bucket (or range of buckets)
|
|
229
|
-
double nConf = 0; // Number of tx's confirmed within the confTarget
|
|
230
|
-
double totalNum = 0; // Total number of tx's that were ever confirmed
|
|
231
|
-
int extraNum = 0; // Number of tx's still in mempool for confTarget or longer
|
|
232
|
-
double failNum = 0; // Number of tx's that were never confirmed but removed from the mempool after confTarget
|
|
233
|
-
const int periodTarget = (confTarget + scale - 1) / scale;
|
|
234
|
-
const int maxbucketindex = buckets.size() - 1;
|
|
235
|
-
|
|
236
|
-
// We'll combine buckets until we have enough samples.
|
|
237
|
-
// The near and far variables will define the range we've combined
|
|
238
|
-
// The best variables are the last range we saw which still had a high
|
|
239
|
-
// enough confirmation rate to count as success.
|
|
240
|
-
// The cur variables are the current range we're counting.
|
|
241
|
-
unsigned int curNearBucket = maxbucketindex;
|
|
242
|
-
unsigned int bestNearBucket = maxbucketindex;
|
|
243
|
-
unsigned int curFarBucket = maxbucketindex;
|
|
244
|
-
unsigned int bestFarBucket = maxbucketindex;
|
|
245
|
-
|
|
246
|
-
bool foundAnswer = false;
|
|
247
|
-
unsigned int bins = unconfTxs.size();
|
|
248
|
-
bool newBucketRange = true;
|
|
249
|
-
bool passing = true;
|
|
250
|
-
EstimatorBucket passBucket;
|
|
251
|
-
EstimatorBucket failBucket;
|
|
252
|
-
|
|
253
|
-
// Start counting from highest feerate transactions
|
|
254
|
-
for (int bucket = maxbucketindex; bucket >= 0; --bucket) {
|
|
255
|
-
if (newBucketRange) {
|
|
256
|
-
curNearBucket = bucket;
|
|
257
|
-
newBucketRange = false;
|
|
258
|
-
}
|
|
259
|
-
curFarBucket = bucket;
|
|
260
|
-
nConf += confAvg[periodTarget - 1][bucket];
|
|
261
|
-
totalNum += txCtAvg[bucket];
|
|
262
|
-
failNum += failAvg[periodTarget - 1][bucket];
|
|
263
|
-
for (unsigned int confct = confTarget; confct < GetMaxConfirms(); confct++)
|
|
264
|
-
extraNum += unconfTxs[(nBlockHeight - confct) % bins][bucket];
|
|
265
|
-
extraNum += oldUnconfTxs[bucket];
|
|
266
|
-
// If we have enough transaction data points in this range of buckets,
|
|
267
|
-
// we can test for success
|
|
268
|
-
// (Only count the confirmed data points, so that each confirmation count
|
|
269
|
-
// will be looking at the same amount of data and same bucket breaks)
|
|
270
|
-
if (totalNum >= sufficientTxVal / (1 - decay)) {
|
|
271
|
-
double curPct = nConf / (totalNum + failNum + extraNum);
|
|
272
|
-
|
|
273
|
-
// Check to see if we are no longer getting confirmed at the success rate
|
|
274
|
-
if (curPct < successBreakPoint) {
|
|
275
|
-
if (passing == true) {
|
|
276
|
-
// First time we hit a failure record the failed bucket
|
|
277
|
-
unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
|
|
278
|
-
unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
|
|
279
|
-
failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0;
|
|
280
|
-
failBucket.end = buckets[failMaxBucket];
|
|
281
|
-
failBucket.withinTarget = nConf;
|
|
282
|
-
failBucket.totalConfirmed = totalNum;
|
|
283
|
-
failBucket.inMempool = extraNum;
|
|
284
|
-
failBucket.leftMempool = failNum;
|
|
285
|
-
passing = false;
|
|
286
|
-
}
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
// Otherwise update the cumulative stats, and the bucket variables
|
|
290
|
-
// and reset the counters
|
|
291
|
-
else {
|
|
292
|
-
failBucket = EstimatorBucket(); // Reset any failed bucket, currently passing
|
|
293
|
-
foundAnswer = true;
|
|
294
|
-
passing = true;
|
|
295
|
-
passBucket.withinTarget = nConf;
|
|
296
|
-
nConf = 0;
|
|
297
|
-
passBucket.totalConfirmed = totalNum;
|
|
298
|
-
totalNum = 0;
|
|
299
|
-
passBucket.inMempool = extraNum;
|
|
300
|
-
passBucket.leftMempool = failNum;
|
|
301
|
-
failNum = 0;
|
|
302
|
-
extraNum = 0;
|
|
303
|
-
bestNearBucket = curNearBucket;
|
|
304
|
-
bestFarBucket = curFarBucket;
|
|
305
|
-
newBucketRange = true;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
double median = -1;
|
|
311
|
-
double txSum = 0;
|
|
312
|
-
|
|
313
|
-
// Calculate the "average" feerate of the best bucket range that met success conditions
|
|
314
|
-
// Find the bucket with the median transaction and then report the average feerate from that bucket
|
|
315
|
-
// This is a compromise between finding the median which we can't since we don't save all tx's
|
|
316
|
-
// and reporting the average which is less accurate
|
|
317
|
-
unsigned int minBucket = std::min(bestNearBucket, bestFarBucket);
|
|
318
|
-
unsigned int maxBucket = std::max(bestNearBucket, bestFarBucket);
|
|
319
|
-
for (unsigned int j = minBucket; j <= maxBucket; j++) {
|
|
320
|
-
txSum += txCtAvg[j];
|
|
321
|
-
}
|
|
322
|
-
if (foundAnswer && txSum != 0) {
|
|
323
|
-
txSum = txSum / 2;
|
|
324
|
-
for (unsigned int j = minBucket; j <= maxBucket; j++) {
|
|
325
|
-
if (txCtAvg[j] < txSum)
|
|
326
|
-
txSum -= txCtAvg[j];
|
|
327
|
-
else { // we're in the right bucket
|
|
328
|
-
median = m_feerate_avg[j] / txCtAvg[j];
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
passBucket.start = minBucket ? buckets[minBucket-1] : 0;
|
|
334
|
-
passBucket.end = buckets[maxBucket];
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// If we were passing until we reached last few buckets with insufficient data, then report those as failed
|
|
338
|
-
if (passing && !newBucketRange) {
|
|
339
|
-
unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
|
|
340
|
-
unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
|
|
341
|
-
failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0;
|
|
342
|
-
failBucket.end = buckets[failMaxBucket];
|
|
343
|
-
failBucket.withinTarget = nConf;
|
|
344
|
-
failBucket.totalConfirmed = totalNum;
|
|
345
|
-
failBucket.inMempool = extraNum;
|
|
346
|
-
failBucket.leftMempool = failNum;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
float passed_within_target_perc = 0.0;
|
|
350
|
-
float failed_within_target_perc = 0.0;
|
|
351
|
-
if ((passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool)) {
|
|
352
|
-
passed_within_target_perc = 100 * passBucket.withinTarget / (passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool);
|
|
353
|
-
}
|
|
354
|
-
if ((failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool)) {
|
|
355
|
-
failed_within_target_perc = 100 * failBucket.withinTarget / (failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
LogPrint(BCLog::ESTIMATEFEE, "FeeEst: %d > %.0f%% decay %.5f: feerate: %g from (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
|
|
359
|
-
confTarget, 100.0 * successBreakPoint, decay,
|
|
360
|
-
median, passBucket.start, passBucket.end,
|
|
361
|
-
passed_within_target_perc,
|
|
362
|
-
passBucket.withinTarget, passBucket.totalConfirmed, passBucket.inMempool, passBucket.leftMempool,
|
|
363
|
-
failBucket.start, failBucket.end,
|
|
364
|
-
failed_within_target_perc,
|
|
365
|
-
failBucket.withinTarget, failBucket.totalConfirmed, failBucket.inMempool, failBucket.leftMempool);
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
if (result) {
|
|
369
|
-
result->pass = passBucket;
|
|
370
|
-
result->fail = failBucket;
|
|
371
|
-
result->decay = decay;
|
|
372
|
-
result->scale = scale;
|
|
373
|
-
}
|
|
374
|
-
return median;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
void TxConfirmStats::Write(CAutoFile& fileout) const
|
|
378
|
-
{
|
|
379
|
-
fileout << Using<EncodedDoubleFormatter>(decay);
|
|
380
|
-
fileout << scale;
|
|
381
|
-
fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);
|
|
382
|
-
fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);
|
|
383
|
-
fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);
|
|
384
|
-
fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets)
|
|
388
|
-
{
|
|
389
|
-
// Read data file and do some very basic sanity checking
|
|
390
|
-
// buckets and bucketMap are not updated yet, so don't access them
|
|
391
|
-
// If there is a read failure, we'll just discard this entire object anyway
|
|
392
|
-
size_t maxConfirms, maxPeriods;
|
|
393
|
-
|
|
394
|
-
// The current version will store the decay with each individual TxConfirmStats and also keep a scale factor
|
|
395
|
-
filein >> Using<EncodedDoubleFormatter>(decay);
|
|
396
|
-
if (decay <= 0 || decay >= 1) {
|
|
397
|
-
throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
|
|
398
|
-
}
|
|
399
|
-
filein >> scale;
|
|
400
|
-
if (scale == 0) {
|
|
401
|
-
throw std::runtime_error("Corrupt estimates file. Scale must be non-zero");
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);
|
|
405
|
-
if (m_feerate_avg.size() != numBuckets) {
|
|
406
|
-
throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count");
|
|
407
|
-
}
|
|
408
|
-
filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);
|
|
409
|
-
if (txCtAvg.size() != numBuckets) {
|
|
410
|
-
throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count");
|
|
411
|
-
}
|
|
412
|
-
filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);
|
|
413
|
-
maxPeriods = confAvg.size();
|
|
414
|
-
maxConfirms = scale * maxPeriods;
|
|
415
|
-
|
|
416
|
-
if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) { // one week
|
|
417
|
-
throw std::runtime_error("Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms");
|
|
418
|
-
}
|
|
419
|
-
for (unsigned int i = 0; i < maxPeriods; i++) {
|
|
420
|
-
if (confAvg[i].size() != numBuckets) {
|
|
421
|
-
throw std::runtime_error("Corrupt estimates file. Mismatch in feerate conf average bucket count");
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);
|
|
426
|
-
if (maxPeriods != failAvg.size()) {
|
|
427
|
-
throw std::runtime_error("Corrupt estimates file. Mismatch in confirms tracked for failures");
|
|
428
|
-
}
|
|
429
|
-
for (unsigned int i = 0; i < maxPeriods; i++) {
|
|
430
|
-
if (failAvg[i].size() != numBuckets) {
|
|
431
|
-
throw std::runtime_error("Corrupt estimates file. Mismatch in one of failure average bucket counts");
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Resize the current block variables which aren't stored in the data file
|
|
436
|
-
// to match the number of confirms and buckets
|
|
437
|
-
resizeInMemoryCounters(numBuckets);
|
|
438
|
-
|
|
439
|
-
LogPrint(BCLog::ESTIMATEFEE, "Reading estimates: %u buckets counting confirms up to %u blocks\n",
|
|
440
|
-
numBuckets, maxConfirms);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val)
|
|
444
|
-
{
|
|
445
|
-
unsigned int bucketindex = bucketMap.lower_bound(val)->second;
|
|
446
|
-
unsigned int blockIndex = nBlockHeight % unconfTxs.size();
|
|
447
|
-
unconfTxs[blockIndex][bucketindex]++;
|
|
448
|
-
return bucketindex;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight, unsigned int bucketindex, bool inBlock)
|
|
452
|
-
{
|
|
453
|
-
//nBestSeenHeight is not updated yet for the new block
|
|
454
|
-
int blocksAgo = nBestSeenHeight - entryHeight;
|
|
455
|
-
if (nBestSeenHeight == 0) // the BlockPolicyEstimator hasn't seen any blocks yet
|
|
456
|
-
blocksAgo = 0;
|
|
457
|
-
if (blocksAgo < 0) {
|
|
458
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error, blocks ago is negative for mempool tx\n");
|
|
459
|
-
return; //This can't happen because we call this with our best seen height, no entries can have higher
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (blocksAgo >= (int)unconfTxs.size()) {
|
|
463
|
-
if (oldUnconfTxs[bucketindex] > 0) {
|
|
464
|
-
oldUnconfTxs[bucketindex]--;
|
|
465
|
-
} else {
|
|
466
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from >25 blocks,bucketIndex=%u already\n",
|
|
467
|
-
bucketindex);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
unsigned int blockIndex = entryHeight % unconfTxs.size();
|
|
472
|
-
if (unconfTxs[blockIndex][bucketindex] > 0) {
|
|
473
|
-
unconfTxs[blockIndex][bucketindex]--;
|
|
474
|
-
} else {
|
|
475
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from blockIndex=%u,bucketIndex=%u already\n",
|
|
476
|
-
blockIndex, bucketindex);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
if (!inBlock && (unsigned int)blocksAgo >= scale) { // Only counts as a failure if not confirmed for entire period
|
|
480
|
-
assert(scale != 0);
|
|
481
|
-
unsigned int periodsAgo = blocksAgo / scale;
|
|
482
|
-
for (size_t i = 0; i < periodsAgo && i < failAvg.size(); i++) {
|
|
483
|
-
failAvg[i][bucketindex]++;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// This function is called from CTxMemPool::removeUnchecked to ensure
|
|
489
|
-
// txs removed from the mempool for any reason are no longer
|
|
490
|
-
// tracked. Txs that were part of a block have already been removed in
|
|
491
|
-
// processBlockTx to ensure they are never double tracked, but it is
|
|
492
|
-
// of no harm to try to remove them again.
|
|
493
|
-
bool CBlockPolicyEstimator::removeTx(uint256 hash, bool inBlock)
|
|
494
|
-
{
|
|
495
|
-
LOCK(m_cs_fee_estimator);
|
|
496
|
-
return _removeTx(hash, inBlock);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
bool CBlockPolicyEstimator::_removeTx(const uint256& hash, bool inBlock)
|
|
500
|
-
{
|
|
501
|
-
AssertLockHeld(m_cs_fee_estimator);
|
|
502
|
-
std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
|
|
503
|
-
if (pos != mapMemPoolTxs.end()) {
|
|
504
|
-
feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
|
|
505
|
-
shortStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
|
|
506
|
-
longStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
|
|
507
|
-
mapMemPoolTxs.erase(hash);
|
|
508
|
-
return true;
|
|
509
|
-
} else {
|
|
510
|
-
return false;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
CBlockPolicyEstimator::CBlockPolicyEstimator()
|
|
515
|
-
: nBestSeenHeight(0), firstRecordedHeight(0), historicalFirst(0), historicalBest(0), trackedTxs(0), untrackedTxs(0)
|
|
516
|
-
{
|
|
517
|
-
static_assert(MIN_BUCKET_FEERATE > 0, "Min feerate must be nonzero");
|
|
518
|
-
size_t bucketIndex = 0;
|
|
519
|
-
|
|
520
|
-
for (double bucketBoundary = MIN_BUCKET_FEERATE; bucketBoundary <= MAX_BUCKET_FEERATE; bucketBoundary *= FEE_SPACING, bucketIndex++) {
|
|
521
|
-
buckets.push_back(bucketBoundary);
|
|
522
|
-
bucketMap[bucketBoundary] = bucketIndex;
|
|
523
|
-
}
|
|
524
|
-
buckets.push_back(INF_FEERATE);
|
|
525
|
-
bucketMap[INF_FEERATE] = bucketIndex;
|
|
526
|
-
assert(bucketMap.size() == buckets.size());
|
|
527
|
-
|
|
528
|
-
feeStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
|
|
529
|
-
shortStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
|
|
530
|
-
longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
|
|
531
|
-
|
|
532
|
-
// If the fee estimation file is present, read recorded estimations
|
|
533
|
-
fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME;
|
|
534
|
-
CAutoFile est_file(fsbridge::fopen(est_filepath, "rb"), SER_DISK, CLIENT_VERSION);
|
|
535
|
-
if (est_file.IsNull() || !Read(est_file)) {
|
|
536
|
-
LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", fs::PathToString(est_filepath));
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
CBlockPolicyEstimator::~CBlockPolicyEstimator()
|
|
541
|
-
{
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
|
|
545
|
-
{
|
|
546
|
-
LOCK(m_cs_fee_estimator);
|
|
547
|
-
unsigned int txHeight = entry.GetHeight();
|
|
548
|
-
uint256 hash = entry.GetTx().GetHash();
|
|
549
|
-
if (mapMemPoolTxs.count(hash)) {
|
|
550
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error mempool tx %s already being tracked\n",
|
|
551
|
-
hash.ToString());
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (txHeight != nBestSeenHeight) {
|
|
556
|
-
// Ignore side chains and re-orgs; assuming they are random they don't
|
|
557
|
-
// affect the estimate. We'll potentially double count transactions in 1-block reorgs.
|
|
558
|
-
// Ignore txs if BlockPolicyEstimator is not in sync with ActiveChain().Tip().
|
|
559
|
-
// It will be synced next time a block is processed.
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// Only want to be updating estimates when our blockchain is synced,
|
|
564
|
-
// otherwise we'll miscalculate how many blocks its taking to get included.
|
|
565
|
-
if (!validFeeEstimate) {
|
|
566
|
-
untrackedTxs++;
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
trackedTxs++;
|
|
570
|
-
|
|
571
|
-
// Feerates are stored and reported as BTC-per-kb:
|
|
572
|
-
CFeeRate feeRate(entry.GetFee(), entry.GetTxSize());
|
|
573
|
-
|
|
574
|
-
mapMemPoolTxs[hash].blockHeight = txHeight;
|
|
575
|
-
unsigned int bucketIndex = feeStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
|
|
576
|
-
mapMemPoolTxs[hash].bucketIndex = bucketIndex;
|
|
577
|
-
unsigned int bucketIndex2 = shortStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
|
|
578
|
-
assert(bucketIndex == bucketIndex2);
|
|
579
|
-
unsigned int bucketIndex3 = longStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
|
|
580
|
-
assert(bucketIndex == bucketIndex3);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry)
|
|
584
|
-
{
|
|
585
|
-
AssertLockHeld(m_cs_fee_estimator);
|
|
586
|
-
if (!_removeTx(entry->GetTx().GetHash(), true)) {
|
|
587
|
-
// This transaction wasn't being tracked for fee estimation
|
|
588
|
-
return false;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
// How many blocks did it take for miners to include this transaction?
|
|
592
|
-
// blocksToConfirm is 1-based, so a transaction included in the earliest
|
|
593
|
-
// possible block has confirmation count of 1
|
|
594
|
-
int blocksToConfirm = nBlockHeight - entry->GetHeight();
|
|
595
|
-
if (blocksToConfirm <= 0) {
|
|
596
|
-
// This can't happen because we don't process transactions from a block with a height
|
|
597
|
-
// lower than our greatest seen height
|
|
598
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error Transaction had negative blocksToConfirm\n");
|
|
599
|
-
return false;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// Feerates are stored and reported as BTC-per-kb:
|
|
603
|
-
CFeeRate feeRate(entry->GetFee(), entry->GetTxSize());
|
|
604
|
-
|
|
605
|
-
feeStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
|
|
606
|
-
shortStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
|
|
607
|
-
longStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
|
|
608
|
-
return true;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
|
|
612
|
-
std::vector<const CTxMemPoolEntry*>& entries)
|
|
613
|
-
{
|
|
614
|
-
LOCK(m_cs_fee_estimator);
|
|
615
|
-
if (nBlockHeight <= nBestSeenHeight) {
|
|
616
|
-
// Ignore side chains and re-orgs; assuming they are random
|
|
617
|
-
// they don't affect the estimate.
|
|
618
|
-
// And if an attacker can re-org the chain at will, then
|
|
619
|
-
// you've got much bigger problems than "attacker can influence
|
|
620
|
-
// transaction fees."
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Must update nBestSeenHeight in sync with ClearCurrent so that
|
|
625
|
-
// calls to removeTx (via processBlockTx) correctly calculate age
|
|
626
|
-
// of unconfirmed txs to remove from tracking.
|
|
627
|
-
nBestSeenHeight = nBlockHeight;
|
|
628
|
-
|
|
629
|
-
// Update unconfirmed circular buffer
|
|
630
|
-
feeStats->ClearCurrent(nBlockHeight);
|
|
631
|
-
shortStats->ClearCurrent(nBlockHeight);
|
|
632
|
-
longStats->ClearCurrent(nBlockHeight);
|
|
633
|
-
|
|
634
|
-
// Decay all exponential averages
|
|
635
|
-
feeStats->UpdateMovingAverages();
|
|
636
|
-
shortStats->UpdateMovingAverages();
|
|
637
|
-
longStats->UpdateMovingAverages();
|
|
638
|
-
|
|
639
|
-
unsigned int countedTxs = 0;
|
|
640
|
-
// Update averages with data points from current block
|
|
641
|
-
for (const auto& entry : entries) {
|
|
642
|
-
if (processBlockTx(nBlockHeight, entry))
|
|
643
|
-
countedTxs++;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
if (firstRecordedHeight == 0 && countedTxs > 0) {
|
|
647
|
-
firstRecordedHeight = nBestSeenHeight;
|
|
648
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy first recorded height %u\n", firstRecordedHeight);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy estimates updated by %u of %u block txs, since last block %u of %u tracked, mempool map size %u, max target %u from %s\n",
|
|
653
|
-
countedTxs, entries.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size(),
|
|
654
|
-
MaxUsableEstimate(), HistoricalBlockSpan() > BlockSpan() ? "historical" : "current");
|
|
655
|
-
|
|
656
|
-
trackedTxs = 0;
|
|
657
|
-
untrackedTxs = 0;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) const
|
|
661
|
-
{
|
|
662
|
-
// It's not possible to get reasonable estimates for confTarget of 1
|
|
663
|
-
if (confTarget <= 1)
|
|
664
|
-
return CFeeRate(0);
|
|
665
|
-
|
|
666
|
-
return estimateRawFee(confTarget, DOUBLE_SUCCESS_PCT, FeeEstimateHorizon::MED_HALFLIFE);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon, EstimationResult* result) const
|
|
670
|
-
{
|
|
671
|
-
TxConfirmStats* stats = nullptr;
|
|
672
|
-
double sufficientTxs = SUFFICIENT_FEETXS;
|
|
673
|
-
switch (horizon) {
|
|
674
|
-
case FeeEstimateHorizon::SHORT_HALFLIFE: {
|
|
675
|
-
stats = shortStats.get();
|
|
676
|
-
sufficientTxs = SUFFICIENT_TXS_SHORT;
|
|
677
|
-
break;
|
|
678
|
-
}
|
|
679
|
-
case FeeEstimateHorizon::MED_HALFLIFE: {
|
|
680
|
-
stats = feeStats.get();
|
|
681
|
-
break;
|
|
682
|
-
}
|
|
683
|
-
case FeeEstimateHorizon::LONG_HALFLIFE: {
|
|
684
|
-
stats = longStats.get();
|
|
685
|
-
break;
|
|
686
|
-
}
|
|
687
|
-
} // no default case, so the compiler can warn about missing cases
|
|
688
|
-
assert(stats);
|
|
689
|
-
|
|
690
|
-
LOCK(m_cs_fee_estimator);
|
|
691
|
-
// Return failure if trying to analyze a target we're not tracking
|
|
692
|
-
if (confTarget <= 0 || (unsigned int)confTarget > stats->GetMaxConfirms())
|
|
693
|
-
return CFeeRate(0);
|
|
694
|
-
if (successThreshold > 1)
|
|
695
|
-
return CFeeRate(0);
|
|
696
|
-
|
|
697
|
-
double median = stats->EstimateMedianVal(confTarget, sufficientTxs, successThreshold, nBestSeenHeight, result);
|
|
698
|
-
|
|
699
|
-
if (median < 0)
|
|
700
|
-
return CFeeRate(0);
|
|
701
|
-
|
|
702
|
-
return CFeeRate(llround(median));
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const
|
|
706
|
-
{
|
|
707
|
-
LOCK(m_cs_fee_estimator);
|
|
708
|
-
switch (horizon) {
|
|
709
|
-
case FeeEstimateHorizon::SHORT_HALFLIFE: {
|
|
710
|
-
return shortStats->GetMaxConfirms();
|
|
711
|
-
}
|
|
712
|
-
case FeeEstimateHorizon::MED_HALFLIFE: {
|
|
713
|
-
return feeStats->GetMaxConfirms();
|
|
714
|
-
}
|
|
715
|
-
case FeeEstimateHorizon::LONG_HALFLIFE: {
|
|
716
|
-
return longStats->GetMaxConfirms();
|
|
717
|
-
}
|
|
718
|
-
} // no default case, so the compiler can warn about missing cases
|
|
719
|
-
assert(false);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
unsigned int CBlockPolicyEstimator::BlockSpan() const
|
|
723
|
-
{
|
|
724
|
-
if (firstRecordedHeight == 0) return 0;
|
|
725
|
-
assert(nBestSeenHeight >= firstRecordedHeight);
|
|
726
|
-
|
|
727
|
-
return nBestSeenHeight - firstRecordedHeight;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
unsigned int CBlockPolicyEstimator::HistoricalBlockSpan() const
|
|
731
|
-
{
|
|
732
|
-
if (historicalFirst == 0) return 0;
|
|
733
|
-
assert(historicalBest >= historicalFirst);
|
|
734
|
-
|
|
735
|
-
if (nBestSeenHeight - historicalBest > OLDEST_ESTIMATE_HISTORY) return 0;
|
|
736
|
-
|
|
737
|
-
return historicalBest - historicalFirst;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
unsigned int CBlockPolicyEstimator::MaxUsableEstimate() const
|
|
741
|
-
{
|
|
742
|
-
// Block spans are divided by 2 to make sure there are enough potential failing data points for the estimate
|
|
743
|
-
return std::min(longStats->GetMaxConfirms(), std::max(BlockSpan(), HistoricalBlockSpan()) / 2);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/** Return a fee estimate at the required successThreshold from the shortest
|
|
747
|
-
* time horizon which tracks confirmations up to the desired target. If
|
|
748
|
-
* checkShorterHorizon is requested, also allow short time horizon estimates
|
|
749
|
-
* for a lower target to reduce the given answer */
|
|
750
|
-
double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const
|
|
751
|
-
{
|
|
752
|
-
double estimate = -1;
|
|
753
|
-
if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
|
|
754
|
-
// Find estimate from shortest time horizon possible
|
|
755
|
-
if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon
|
|
756
|
-
estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, result);
|
|
757
|
-
}
|
|
758
|
-
else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon
|
|
759
|
-
estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
|
|
760
|
-
}
|
|
761
|
-
else { // long horizon
|
|
762
|
-
estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
|
|
763
|
-
}
|
|
764
|
-
if (checkShorterHorizon) {
|
|
765
|
-
EstimationResult tempResult;
|
|
766
|
-
// If a lower confTarget from a more recent horizon returns a lower answer use it.
|
|
767
|
-
if (confTarget > feeStats->GetMaxConfirms()) {
|
|
768
|
-
double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, &tempResult);
|
|
769
|
-
if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
|
|
770
|
-
estimate = medMax;
|
|
771
|
-
if (result) *result = tempResult;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
if (confTarget > shortStats->GetMaxConfirms()) {
|
|
775
|
-
double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, &tempResult);
|
|
776
|
-
if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
|
|
777
|
-
estimate = shortMax;
|
|
778
|
-
if (result) *result = tempResult;
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
return estimate;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
/** Ensure that for a conservative estimate, the DOUBLE_SUCCESS_PCT is also met
|
|
787
|
-
* at 2 * target for any longer time horizons.
|
|
788
|
-
*/
|
|
789
|
-
double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const
|
|
790
|
-
{
|
|
791
|
-
double estimate = -1;
|
|
792
|
-
EstimationResult tempResult;
|
|
793
|
-
if (doubleTarget <= shortStats->GetMaxConfirms()) {
|
|
794
|
-
estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, result);
|
|
795
|
-
}
|
|
796
|
-
if (doubleTarget <= feeStats->GetMaxConfirms()) {
|
|
797
|
-
double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, &tempResult);
|
|
798
|
-
if (longEstimate > estimate) {
|
|
799
|
-
estimate = longEstimate;
|
|
800
|
-
if (result) *result = tempResult;
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
return estimate;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
/** estimateSmartFee returns the max of the feerates calculated with a 60%
|
|
807
|
-
* threshold required at target / 2, an 85% threshold required at target and a
|
|
808
|
-
* 95% threshold required at 2 * target. Each calculation is performed at the
|
|
809
|
-
* shortest time horizon which tracks the required target. Conservative
|
|
810
|
-
* estimates, however, required the 95% threshold at 2 * target be met for any
|
|
811
|
-
* longer time horizons also.
|
|
812
|
-
*/
|
|
813
|
-
CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const
|
|
814
|
-
{
|
|
815
|
-
LOCK(m_cs_fee_estimator);
|
|
816
|
-
|
|
817
|
-
if (feeCalc) {
|
|
818
|
-
feeCalc->desiredTarget = confTarget;
|
|
819
|
-
feeCalc->returnedTarget = confTarget;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
double median = -1;
|
|
823
|
-
EstimationResult tempResult;
|
|
824
|
-
|
|
825
|
-
// Return failure if trying to analyze a target we're not tracking
|
|
826
|
-
if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms()) {
|
|
827
|
-
return CFeeRate(0); // error condition
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
// It's not possible to get reasonable estimates for confTarget of 1
|
|
831
|
-
if (confTarget == 1) confTarget = 2;
|
|
832
|
-
|
|
833
|
-
unsigned int maxUsableEstimate = MaxUsableEstimate();
|
|
834
|
-
if ((unsigned int)confTarget > maxUsableEstimate) {
|
|
835
|
-
confTarget = maxUsableEstimate;
|
|
836
|
-
}
|
|
837
|
-
if (feeCalc) feeCalc->returnedTarget = confTarget;
|
|
838
|
-
|
|
839
|
-
if (confTarget <= 1) return CFeeRate(0); // error condition
|
|
840
|
-
|
|
841
|
-
assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints
|
|
842
|
-
/** true is passed to estimateCombined fee for target/2 and target so
|
|
843
|
-
* that we check the max confirms for shorter time horizons as well.
|
|
844
|
-
* This is necessary to preserve monotonically increasing estimates.
|
|
845
|
-
* For non-conservative estimates we do the same thing for 2*target, but
|
|
846
|
-
* for conservative estimates we want to skip these shorter horizons
|
|
847
|
-
* checks for 2*target because we are taking the max over all time
|
|
848
|
-
* horizons so we already have monotonically increasing estimates and
|
|
849
|
-
* the purpose of conservative estimates is not to let short term
|
|
850
|
-
* fluctuations lower our estimates by too much.
|
|
851
|
-
*/
|
|
852
|
-
double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true, &tempResult);
|
|
853
|
-
if (feeCalc) {
|
|
854
|
-
feeCalc->est = tempResult;
|
|
855
|
-
feeCalc->reason = FeeReason::HALF_ESTIMATE;
|
|
856
|
-
}
|
|
857
|
-
median = halfEst;
|
|
858
|
-
double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true, &tempResult);
|
|
859
|
-
if (actualEst > median) {
|
|
860
|
-
median = actualEst;
|
|
861
|
-
if (feeCalc) {
|
|
862
|
-
feeCalc->est = tempResult;
|
|
863
|
-
feeCalc->reason = FeeReason::FULL_ESTIMATE;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult);
|
|
867
|
-
if (doubleEst > median) {
|
|
868
|
-
median = doubleEst;
|
|
869
|
-
if (feeCalc) {
|
|
870
|
-
feeCalc->est = tempResult;
|
|
871
|
-
feeCalc->reason = FeeReason::DOUBLE_ESTIMATE;
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
if (conservative || median == -1) {
|
|
876
|
-
double consEst = estimateConservativeFee(2 * confTarget, &tempResult);
|
|
877
|
-
if (consEst > median) {
|
|
878
|
-
median = consEst;
|
|
879
|
-
if (feeCalc) {
|
|
880
|
-
feeCalc->est = tempResult;
|
|
881
|
-
feeCalc->reason = FeeReason::CONSERVATIVE;
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
if (median < 0) return CFeeRate(0); // error condition
|
|
887
|
-
|
|
888
|
-
return CFeeRate(llround(median));
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
void CBlockPolicyEstimator::Flush() {
|
|
892
|
-
FlushUnconfirmed();
|
|
893
|
-
|
|
894
|
-
fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME;
|
|
895
|
-
CAutoFile est_file(fsbridge::fopen(est_filepath, "wb"), SER_DISK, CLIENT_VERSION);
|
|
896
|
-
if (est_file.IsNull() || !Write(est_file)) {
|
|
897
|
-
LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", fs::PathToString(est_filepath));
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const
|
|
902
|
-
{
|
|
903
|
-
try {
|
|
904
|
-
LOCK(m_cs_fee_estimator);
|
|
905
|
-
fileout << 149900; // version required to read: 0.14.99 or later
|
|
906
|
-
fileout << CLIENT_VERSION; // version that wrote the file
|
|
907
|
-
fileout << nBestSeenHeight;
|
|
908
|
-
if (BlockSpan() > HistoricalBlockSpan()/2) {
|
|
909
|
-
fileout << firstRecordedHeight << nBestSeenHeight;
|
|
910
|
-
}
|
|
911
|
-
else {
|
|
912
|
-
fileout << historicalFirst << historicalBest;
|
|
913
|
-
}
|
|
914
|
-
fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(buckets);
|
|
915
|
-
feeStats->Write(fileout);
|
|
916
|
-
shortStats->Write(fileout);
|
|
917
|
-
longStats->Write(fileout);
|
|
918
|
-
}
|
|
919
|
-
catch (const std::exception&) {
|
|
920
|
-
LogPrintf("CBlockPolicyEstimator::Write(): unable to write policy estimator data (non-fatal)\n");
|
|
921
|
-
return false;
|
|
922
|
-
}
|
|
923
|
-
return true;
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
bool CBlockPolicyEstimator::Read(CAutoFile& filein)
|
|
927
|
-
{
|
|
928
|
-
try {
|
|
929
|
-
LOCK(m_cs_fee_estimator);
|
|
930
|
-
int nVersionRequired, nVersionThatWrote;
|
|
931
|
-
filein >> nVersionRequired >> nVersionThatWrote;
|
|
932
|
-
if (nVersionRequired > CLIENT_VERSION) {
|
|
933
|
-
throw std::runtime_error(strprintf("up-version (%d) fee estimate file", nVersionRequired));
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
// Read fee estimates file into temporary variables so existing data
|
|
937
|
-
// structures aren't corrupted if there is an exception.
|
|
938
|
-
unsigned int nFileBestSeenHeight;
|
|
939
|
-
filein >> nFileBestSeenHeight;
|
|
940
|
-
|
|
941
|
-
if (nVersionRequired < 149900) {
|
|
942
|
-
LogPrintf("%s: incompatible old fee estimation data (non-fatal). Version: %d\n", __func__, nVersionRequired);
|
|
943
|
-
} else { // New format introduced in 149900
|
|
944
|
-
unsigned int nFileHistoricalFirst, nFileHistoricalBest;
|
|
945
|
-
filein >> nFileHistoricalFirst >> nFileHistoricalBest;
|
|
946
|
-
if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) {
|
|
947
|
-
throw std::runtime_error("Corrupt estimates file. Historical block range for estimates is invalid");
|
|
948
|
-
}
|
|
949
|
-
std::vector<double> fileBuckets;
|
|
950
|
-
filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(fileBuckets);
|
|
951
|
-
size_t numBuckets = fileBuckets.size();
|
|
952
|
-
if (numBuckets <= 1 || numBuckets > 1000) {
|
|
953
|
-
throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
std::unique_ptr<TxConfirmStats> fileFeeStats(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
|
|
957
|
-
std::unique_ptr<TxConfirmStats> fileShortStats(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
|
|
958
|
-
std::unique_ptr<TxConfirmStats> fileLongStats(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
|
|
959
|
-
fileFeeStats->Read(filein, nVersionThatWrote, numBuckets);
|
|
960
|
-
fileShortStats->Read(filein, nVersionThatWrote, numBuckets);
|
|
961
|
-
fileLongStats->Read(filein, nVersionThatWrote, numBuckets);
|
|
962
|
-
|
|
963
|
-
// Fee estimates file parsed correctly
|
|
964
|
-
// Copy buckets from file and refresh our bucketmap
|
|
965
|
-
buckets = fileBuckets;
|
|
966
|
-
bucketMap.clear();
|
|
967
|
-
for (unsigned int i = 0; i < buckets.size(); i++) {
|
|
968
|
-
bucketMap[buckets[i]] = i;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
// Destroy old TxConfirmStats and point to new ones that already reference buckets and bucketMap
|
|
972
|
-
feeStats = std::move(fileFeeStats);
|
|
973
|
-
shortStats = std::move(fileShortStats);
|
|
974
|
-
longStats = std::move(fileLongStats);
|
|
975
|
-
|
|
976
|
-
nBestSeenHeight = nFileBestSeenHeight;
|
|
977
|
-
historicalFirst = nFileHistoricalFirst;
|
|
978
|
-
historicalBest = nFileHistoricalBest;
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
catch (const std::exception& e) {
|
|
982
|
-
LogPrintf("CBlockPolicyEstimator::Read(): unable to read policy estimator data (non-fatal): %s\n",e.what());
|
|
983
|
-
return false;
|
|
984
|
-
}
|
|
985
|
-
return true;
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
void CBlockPolicyEstimator::FlushUnconfirmed() {
|
|
989
|
-
int64_t startclear = GetTimeMicros();
|
|
990
|
-
LOCK(m_cs_fee_estimator);
|
|
991
|
-
size_t num_entries = mapMemPoolTxs.size();
|
|
992
|
-
// Remove every entry in mapMemPoolTxs
|
|
993
|
-
while (!mapMemPoolTxs.empty()) {
|
|
994
|
-
auto mi = mapMemPoolTxs.begin();
|
|
995
|
-
_removeTx(mi->first, false); // this calls erase() on mapMemPoolTxs
|
|
996
|
-
}
|
|
997
|
-
int64_t endclear = GetTimeMicros();
|
|
998
|
-
LogPrint(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %gs\n", num_entries, (endclear - startclear)*0.000001);
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee)
|
|
1002
|
-
{
|
|
1003
|
-
CAmount minFeeLimit = std::max(CAmount(1), minIncrementalFee.GetFeePerK() / 2);
|
|
1004
|
-
feeset.insert(0);
|
|
1005
|
-
for (double bucketBoundary = minFeeLimit; bucketBoundary <= MAX_FILTER_FEERATE; bucketBoundary *= FEE_FILTER_SPACING) {
|
|
1006
|
-
feeset.insert(bucketBoundary);
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
CAmount FeeFilterRounder::round(CAmount currentMinFee)
|
|
1011
|
-
{
|
|
1012
|
-
std::set<double>::iterator it = feeset.lower_bound(currentMinFee);
|
|
1013
|
-
if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) {
|
|
1014
|
-
it--;
|
|
1015
|
-
}
|
|
1016
|
-
return static_cast<CAmount>(*it);
|
|
1017
|
-
}
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
-
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
|
3
|
-
// Distributed under the MIT software license, see the accompanying
|
|
4
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
-
#ifndef BITCOIN_POLICY_FEES_H
|
|
6
|
-
#define BITCOIN_POLICY_FEES_H
|
|
7
|
-
|
|
8
|
-
#include <consensus/amount.h>
|
|
9
|
-
#include <policy/feerate.h>
|
|
10
|
-
#include <uint256.h>
|
|
11
|
-
#include <random.h>
|
|
12
|
-
#include <sync.h>
|
|
13
|
-
|
|
14
|
-
#include <array>
|
|
15
|
-
#include <map>
|
|
16
|
-
#include <memory>
|
|
17
|
-
#include <string>
|
|
18
|
-
#include <vector>
|
|
19
|
-
|
|
20
|
-
class CAutoFile;
|
|
21
|
-
class CFeeRate;
|
|
22
|
-
class CTxMemPoolEntry;
|
|
23
|
-
class CTxMemPool;
|
|
24
|
-
class TxConfirmStats;
|
|
25
|
-
|
|
26
|
-
/* Identifier for each of the 3 different TxConfirmStats which will track
|
|
27
|
-
* history over different time horizons. */
|
|
28
|
-
enum class FeeEstimateHorizon {
|
|
29
|
-
SHORT_HALFLIFE,
|
|
30
|
-
MED_HALFLIFE,
|
|
31
|
-
LONG_HALFLIFE,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
static constexpr auto ALL_FEE_ESTIMATE_HORIZONS = std::array{
|
|
35
|
-
FeeEstimateHorizon::SHORT_HALFLIFE,
|
|
36
|
-
FeeEstimateHorizon::MED_HALFLIFE,
|
|
37
|
-
FeeEstimateHorizon::LONG_HALFLIFE,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon);
|
|
41
|
-
|
|
42
|
-
/* Enumeration of reason for returned fee estimate */
|
|
43
|
-
enum class FeeReason {
|
|
44
|
-
NONE,
|
|
45
|
-
HALF_ESTIMATE,
|
|
46
|
-
FULL_ESTIMATE,
|
|
47
|
-
DOUBLE_ESTIMATE,
|
|
48
|
-
CONSERVATIVE,
|
|
49
|
-
MEMPOOL_MIN,
|
|
50
|
-
PAYTXFEE,
|
|
51
|
-
FALLBACK,
|
|
52
|
-
REQUIRED,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/* Used to return detailed information about a feerate bucket */
|
|
56
|
-
struct EstimatorBucket
|
|
57
|
-
{
|
|
58
|
-
double start = -1;
|
|
59
|
-
double end = -1;
|
|
60
|
-
double withinTarget = 0;
|
|
61
|
-
double totalConfirmed = 0;
|
|
62
|
-
double inMempool = 0;
|
|
63
|
-
double leftMempool = 0;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/* Used to return detailed information about a fee estimate calculation */
|
|
67
|
-
struct EstimationResult
|
|
68
|
-
{
|
|
69
|
-
EstimatorBucket pass;
|
|
70
|
-
EstimatorBucket fail;
|
|
71
|
-
double decay = 0;
|
|
72
|
-
unsigned int scale = 0;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
struct FeeCalculation
|
|
76
|
-
{
|
|
77
|
-
EstimationResult est;
|
|
78
|
-
FeeReason reason = FeeReason::NONE;
|
|
79
|
-
int desiredTarget = 0;
|
|
80
|
-
int returnedTarget = 0;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
/** \class CBlockPolicyEstimator
|
|
84
|
-
* The BlockPolicyEstimator is used for estimating the feerate needed
|
|
85
|
-
* for a transaction to be included in a block within a certain number of
|
|
86
|
-
* blocks.
|
|
87
|
-
*
|
|
88
|
-
* At a high level the algorithm works by grouping transactions into buckets
|
|
89
|
-
* based on having similar feerates and then tracking how long it
|
|
90
|
-
* takes transactions in the various buckets to be mined. It operates under
|
|
91
|
-
* the assumption that in general transactions of higher feerate will be
|
|
92
|
-
* included in blocks before transactions of lower feerate. So for
|
|
93
|
-
* example if you wanted to know what feerate you should put on a transaction to
|
|
94
|
-
* be included in a block within the next 5 blocks, you would start by looking
|
|
95
|
-
* at the bucket with the highest feerate transactions and verifying that a
|
|
96
|
-
* sufficiently high percentage of them were confirmed within 5 blocks and
|
|
97
|
-
* then you would look at the next highest feerate bucket, and so on, stopping at
|
|
98
|
-
* the last bucket to pass the test. The average feerate of transactions in this
|
|
99
|
-
* bucket will give you an indication of the lowest feerate you can put on a
|
|
100
|
-
* transaction and still have a sufficiently high chance of being confirmed
|
|
101
|
-
* within your desired 5 blocks.
|
|
102
|
-
*
|
|
103
|
-
* Here is a brief description of the implementation:
|
|
104
|
-
* When a transaction enters the mempool, we track the height of the block chain
|
|
105
|
-
* at entry. All further calculations are conducted only on this set of "seen"
|
|
106
|
-
* transactions. Whenever a block comes in, we count the number of transactions
|
|
107
|
-
* in each bucket and the total amount of feerate paid in each bucket. Then we
|
|
108
|
-
* calculate how many blocks Y it took each transaction to be mined. We convert
|
|
109
|
-
* from a number of blocks to a number of periods Y' each encompassing "scale"
|
|
110
|
-
* blocks. This is tracked in 3 different data sets each up to a maximum
|
|
111
|
-
* number of periods. Within each data set we have an array of counters in each
|
|
112
|
-
* feerate bucket and we increment all the counters from Y' up to max periods
|
|
113
|
-
* representing that a tx was successfully confirmed in less than or equal to
|
|
114
|
-
* that many periods. We want to save a history of this information, so at any
|
|
115
|
-
* time we have a counter of the total number of transactions that happened in a
|
|
116
|
-
* given feerate bucket and the total number that were confirmed in each of the
|
|
117
|
-
* periods or less for any bucket. We save this history by keeping an
|
|
118
|
-
* exponentially decaying moving average of each one of these stats. This is
|
|
119
|
-
* done for a different decay in each of the 3 data sets to keep relevant data
|
|
120
|
-
* from different time horizons. Furthermore we also keep track of the number
|
|
121
|
-
* unmined (in mempool or left mempool without being included in a block)
|
|
122
|
-
* transactions in each bucket and for how many blocks they have been
|
|
123
|
-
* outstanding and use both of these numbers to increase the number of transactions
|
|
124
|
-
* we've seen in that feerate bucket when calculating an estimate for any number
|
|
125
|
-
* of confirmations below the number of blocks they've been outstanding.
|
|
126
|
-
*
|
|
127
|
-
* We want to be able to estimate feerates that are needed on tx's to be included in
|
|
128
|
-
* a certain number of blocks. Every time a block is added to the best chain, this class records
|
|
129
|
-
* stats on the transactions included in that block
|
|
130
|
-
*/
|
|
131
|
-
class CBlockPolicyEstimator
|
|
132
|
-
{
|
|
133
|
-
private:
|
|
134
|
-
/** Track confirm delays up to 12 blocks for short horizon */
|
|
135
|
-
static constexpr unsigned int SHORT_BLOCK_PERIODS = 12;
|
|
136
|
-
static constexpr unsigned int SHORT_SCALE = 1;
|
|
137
|
-
/** Track confirm delays up to 48 blocks for medium horizon */
|
|
138
|
-
static constexpr unsigned int MED_BLOCK_PERIODS = 24;
|
|
139
|
-
static constexpr unsigned int MED_SCALE = 2;
|
|
140
|
-
/** Track confirm delays up to 1008 blocks for long horizon */
|
|
141
|
-
static constexpr unsigned int LONG_BLOCK_PERIODS = 42;
|
|
142
|
-
static constexpr unsigned int LONG_SCALE = 24;
|
|
143
|
-
/** Historical estimates that are older than this aren't valid */
|
|
144
|
-
static const unsigned int OLDEST_ESTIMATE_HISTORY = 6 * 1008;
|
|
145
|
-
|
|
146
|
-
/** Decay of .962 is a half-life of 18 blocks or about 3 hours */
|
|
147
|
-
static constexpr double SHORT_DECAY = .962;
|
|
148
|
-
/** Decay of .9952 is a half-life of 144 blocks or about 1 day */
|
|
149
|
-
static constexpr double MED_DECAY = .9952;
|
|
150
|
-
/** Decay of .99931 is a half-life of 1008 blocks or about 1 week */
|
|
151
|
-
static constexpr double LONG_DECAY = .99931;
|
|
152
|
-
|
|
153
|
-
/** Require greater than 60% of X feerate transactions to be confirmed within Y/2 blocks*/
|
|
154
|
-
static constexpr double HALF_SUCCESS_PCT = .6;
|
|
155
|
-
/** Require greater than 85% of X feerate transactions to be confirmed within Y blocks*/
|
|
156
|
-
static constexpr double SUCCESS_PCT = .85;
|
|
157
|
-
/** Require greater than 95% of X feerate transactions to be confirmed within 2 * Y blocks*/
|
|
158
|
-
static constexpr double DOUBLE_SUCCESS_PCT = .95;
|
|
159
|
-
|
|
160
|
-
/** Require an avg of 0.1 tx in the combined feerate bucket per block to have stat significance */
|
|
161
|
-
static constexpr double SUFFICIENT_FEETXS = 0.1;
|
|
162
|
-
/** Require an avg of 0.5 tx when using short decay since there are fewer blocks considered*/
|
|
163
|
-
static constexpr double SUFFICIENT_TXS_SHORT = 0.5;
|
|
164
|
-
|
|
165
|
-
/** Minimum and Maximum values for tracking feerates
|
|
166
|
-
* The MIN_BUCKET_FEERATE should just be set to the lowest reasonable feerate we
|
|
167
|
-
* might ever want to track. Historically this has been 1000 since it was
|
|
168
|
-
* inheriting DEFAULT_MIN_RELAY_TX_FEE and changing it is disruptive as it
|
|
169
|
-
* invalidates old estimates files. So leave it at 1000 unless it becomes
|
|
170
|
-
* necessary to lower it, and then lower it substantially.
|
|
171
|
-
*/
|
|
172
|
-
static constexpr double MIN_BUCKET_FEERATE = 1000;
|
|
173
|
-
static constexpr double MAX_BUCKET_FEERATE = 1e7;
|
|
174
|
-
|
|
175
|
-
/** Spacing of FeeRate buckets
|
|
176
|
-
* We have to lump transactions into buckets based on feerate, but we want to be able
|
|
177
|
-
* to give accurate estimates over a large range of potential feerates
|
|
178
|
-
* Therefore it makes sense to exponentially space the buckets
|
|
179
|
-
*/
|
|
180
|
-
static constexpr double FEE_SPACING = 1.05;
|
|
181
|
-
|
|
182
|
-
public:
|
|
183
|
-
/** Create new BlockPolicyEstimator and initialize stats tracking classes with default values */
|
|
184
|
-
CBlockPolicyEstimator();
|
|
185
|
-
~CBlockPolicyEstimator();
|
|
186
|
-
|
|
187
|
-
/** Process all the transactions that have been included in a block */
|
|
188
|
-
void processBlock(unsigned int nBlockHeight,
|
|
189
|
-
std::vector<const CTxMemPoolEntry*>& entries)
|
|
190
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
191
|
-
|
|
192
|
-
/** Process a transaction accepted to the mempool*/
|
|
193
|
-
void processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
|
|
194
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
195
|
-
|
|
196
|
-
/** Remove a transaction from the mempool tracking stats*/
|
|
197
|
-
bool removeTx(uint256 hash, bool inBlock)
|
|
198
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
199
|
-
|
|
200
|
-
/** DEPRECATED. Return a feerate estimate */
|
|
201
|
-
CFeeRate estimateFee(int confTarget) const
|
|
202
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
203
|
-
|
|
204
|
-
/** Estimate feerate needed to get be included in a block within confTarget
|
|
205
|
-
* blocks. If no answer can be given at confTarget, return an estimate at
|
|
206
|
-
* the closest target where one can be given. 'conservative' estimates are
|
|
207
|
-
* valid over longer time horizons also.
|
|
208
|
-
*/
|
|
209
|
-
CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const
|
|
210
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
211
|
-
|
|
212
|
-
/** Return a specific fee estimate calculation with a given success
|
|
213
|
-
* threshold and time horizon, and optionally return detailed data about
|
|
214
|
-
* calculation
|
|
215
|
-
*/
|
|
216
|
-
CFeeRate estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon,
|
|
217
|
-
EstimationResult* result = nullptr) const
|
|
218
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
219
|
-
|
|
220
|
-
/** Write estimation data to a file */
|
|
221
|
-
bool Write(CAutoFile& fileout) const
|
|
222
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
223
|
-
|
|
224
|
-
/** Read estimation data from a file */
|
|
225
|
-
bool Read(CAutoFile& filein)
|
|
226
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
227
|
-
|
|
228
|
-
/** Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool */
|
|
229
|
-
void FlushUnconfirmed()
|
|
230
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
231
|
-
|
|
232
|
-
/** Calculation of highest target that estimates are tracked for */
|
|
233
|
-
unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const
|
|
234
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
235
|
-
|
|
236
|
-
/** Drop still unconfirmed transactions and record current estimations, if the fee estimation file is present. */
|
|
237
|
-
void Flush()
|
|
238
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
239
|
-
|
|
240
|
-
private:
|
|
241
|
-
mutable Mutex m_cs_fee_estimator;
|
|
242
|
-
|
|
243
|
-
unsigned int nBestSeenHeight GUARDED_BY(m_cs_fee_estimator);
|
|
244
|
-
unsigned int firstRecordedHeight GUARDED_BY(m_cs_fee_estimator);
|
|
245
|
-
unsigned int historicalFirst GUARDED_BY(m_cs_fee_estimator);
|
|
246
|
-
unsigned int historicalBest GUARDED_BY(m_cs_fee_estimator);
|
|
247
|
-
|
|
248
|
-
struct TxStatsInfo
|
|
249
|
-
{
|
|
250
|
-
unsigned int blockHeight;
|
|
251
|
-
unsigned int bucketIndex;
|
|
252
|
-
TxStatsInfo() : blockHeight(0), bucketIndex(0) {}
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
// map of txids to information about that transaction
|
|
256
|
-
std::map<uint256, TxStatsInfo> mapMemPoolTxs GUARDED_BY(m_cs_fee_estimator);
|
|
257
|
-
|
|
258
|
-
/** Classes to track historical data on transaction confirmations */
|
|
259
|
-
std::unique_ptr<TxConfirmStats> feeStats PT_GUARDED_BY(m_cs_fee_estimator);
|
|
260
|
-
std::unique_ptr<TxConfirmStats> shortStats PT_GUARDED_BY(m_cs_fee_estimator);
|
|
261
|
-
std::unique_ptr<TxConfirmStats> longStats PT_GUARDED_BY(m_cs_fee_estimator);
|
|
262
|
-
|
|
263
|
-
unsigned int trackedTxs GUARDED_BY(m_cs_fee_estimator);
|
|
264
|
-
unsigned int untrackedTxs GUARDED_BY(m_cs_fee_estimator);
|
|
265
|
-
|
|
266
|
-
std::vector<double> buckets GUARDED_BY(m_cs_fee_estimator); // The upper-bound of the range for the bucket (inclusive)
|
|
267
|
-
std::map<double, unsigned int> bucketMap GUARDED_BY(m_cs_fee_estimator); // Map of bucket upper-bound to index into all vectors by bucket
|
|
268
|
-
|
|
269
|
-
/** Process a transaction confirmed in a block*/
|
|
270
|
-
bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry) EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
271
|
-
|
|
272
|
-
/** Helper for estimateSmartFee */
|
|
273
|
-
double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
274
|
-
/** Helper for estimateSmartFee */
|
|
275
|
-
double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
276
|
-
/** Number of blocks of data recorded while fee estimates have been running */
|
|
277
|
-
unsigned int BlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
278
|
-
/** Number of blocks of recorded fee estimate data represented in saved data file */
|
|
279
|
-
unsigned int HistoricalBlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
280
|
-
/** Calculation of highest target that reasonable estimate can be provided for */
|
|
281
|
-
unsigned int MaxUsableEstimate() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
282
|
-
|
|
283
|
-
/** A non-thread-safe helper for the removeTx function */
|
|
284
|
-
bool _removeTx(const uint256& hash, bool inBlock)
|
|
285
|
-
EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
class FeeFilterRounder
|
|
289
|
-
{
|
|
290
|
-
private:
|
|
291
|
-
static constexpr double MAX_FILTER_FEERATE = 1e7;
|
|
292
|
-
/** FEE_FILTER_SPACING is just used to provide some quantization of fee
|
|
293
|
-
* filter results. Historically it reused FEE_SPACING, but it is completely
|
|
294
|
-
* unrelated, and was made a separate constant so the two concepts are not
|
|
295
|
-
* tied together */
|
|
296
|
-
static constexpr double FEE_FILTER_SPACING = 1.1;
|
|
297
|
-
|
|
298
|
-
public:
|
|
299
|
-
/** Create new FeeFilterRounder */
|
|
300
|
-
explicit FeeFilterRounder(const CFeeRate& minIncrementalFee);
|
|
301
|
-
|
|
302
|
-
/** Quantize a minimum fee for privacy purpose before broadcast. Not thread-safe due to use of FastRandomContext */
|
|
303
|
-
CAmount round(CAmount currentMinFee);
|
|
304
|
-
|
|
305
|
-
private:
|
|
306
|
-
std::set<double> feeset;
|
|
307
|
-
FastRandomContext insecure_rand;
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
#endif // BITCOIN_POLICY_FEES_H
|
|
@@ -9,52 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
#include <consensus/validation.h>
|
|
11
11
|
#include <coins.h>
|
|
12
|
+
#include <kernel.h>
|
|
12
13
|
#include <span.h>
|
|
13
14
|
|
|
14
|
-
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
|
|
15
|
-
{
|
|
16
|
-
// "Dust" is defined in terms of dustRelayFee,
|
|
17
|
-
// which has units satoshis-per-kilobyte.
|
|
18
|
-
// If you'd pay more in fees than the value of the output
|
|
19
|
-
// to spend something, then we consider it dust.
|
|
20
|
-
// A typical spendable non-segwit txout is 34 bytes big, and will
|
|
21
|
-
// need a CTxIn of at least 148 bytes to spend:
|
|
22
|
-
// so dust is a spendable txout less than
|
|
23
|
-
// 182*dustRelayFee/1000 (in satoshis).
|
|
24
|
-
// 546 satoshis at the default rate of 3000 sat/kvB.
|
|
25
|
-
// A typical spendable segwit P2WPKH txout is 31 bytes big, and will
|
|
26
|
-
// need a CTxIn of at least 67 bytes to spend:
|
|
27
|
-
// so dust is a spendable txout less than
|
|
28
|
-
// 98*dustRelayFee/1000 (in satoshis).
|
|
29
|
-
// 294 satoshis at the default rate of 3000 sat/kvB.
|
|
30
|
-
if (txout.scriptPubKey.IsUnspendable())
|
|
31
|
-
return 0;
|
|
32
|
-
|
|
33
|
-
size_t nSize = GetSerializeSize(txout);
|
|
34
|
-
int witnessversion = 0;
|
|
35
|
-
std::vector<unsigned char> witnessprogram;
|
|
36
|
-
|
|
37
|
-
// Note this computation is for spending a Segwit v0 P2WPKH output (a 33 bytes
|
|
38
|
-
// public key + an ECDSA signature). For Segwit v1 Taproot outputs the minimum
|
|
39
|
-
// satisfaction is lower (a single BIP340 signature) but this computation was
|
|
40
|
-
// kept to not further reduce the dust level.
|
|
41
|
-
// See discussion in https://github.com/bitcoin/bitcoin/pull/22779 for details.
|
|
42
|
-
if (txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
|
|
43
|
-
// sum the sizes of the parts of a transaction input
|
|
44
|
-
// with 75% segwit discount applied to the script size.
|
|
45
|
-
nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
|
|
46
|
-
} else {
|
|
47
|
-
nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return dustRelayFeeIn.GetFee(nSize);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
|
|
54
|
-
{
|
|
55
|
-
return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
15
|
bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType)
|
|
59
16
|
{
|
|
60
17
|
std::vector<std::vector<unsigned char> > vSolutions;
|
|
@@ -78,7 +35,7 @@ bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType)
|
|
|
78
35
|
return true;
|
|
79
36
|
}
|
|
80
37
|
|
|
81
|
-
bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig,
|
|
38
|
+
bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std::string& reason)
|
|
82
39
|
{
|
|
83
40
|
if (tx.nVersion > TX_MAX_STANDARD_VERSION || tx.nVersion < 1) {
|
|
84
41
|
reason = "version";
|
|
@@ -128,9 +85,6 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR
|
|
|
128
85
|
else if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) {
|
|
129
86
|
reason = "bare-multisig";
|
|
130
87
|
return false;
|
|
131
|
-
} else if (IsDust(txout, dust_relay_fee)) {
|
|
132
|
-
reason = "dust";
|
|
133
|
-
return false;
|
|
134
88
|
}
|
|
135
89
|
}
|
|
136
90
|
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
#define BITCOIN_POLICY_POLICY_H
|
|
8
8
|
|
|
9
9
|
#include <consensus/consensus.h>
|
|
10
|
-
#include <policy/feerate.h>
|
|
11
10
|
#include <script/interpreter.h>
|
|
12
11
|
#include <script/standard.h>
|
|
13
12
|
|
|
@@ -18,8 +17,6 @@ class CTxOut;
|
|
|
18
17
|
|
|
19
18
|
/** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/
|
|
20
19
|
static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000;
|
|
21
|
-
/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/
|
|
22
|
-
static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000;
|
|
23
20
|
/** The maximum weight for transactions we're willing to relay/mine */
|
|
24
21
|
static const unsigned int MAX_STANDARD_TX_WEIGHT = 400000;
|
|
25
22
|
/** The minimum non-witness size for transactions we're willing to relay/mine (1 segwit input + 1 P2WPKH output = 82 bytes) */
|
|
@@ -30,8 +27,6 @@ static const unsigned int MAX_P2SH_SIGOPS = 15;
|
|
|
30
27
|
static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5;
|
|
31
28
|
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
|
|
32
29
|
static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
|
|
33
|
-
/** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or BIP 125 replacement **/
|
|
34
|
-
static const unsigned int DEFAULT_INCREMENTAL_RELAY_FEE = 1000;
|
|
35
30
|
/** Default for -bytespersigop */
|
|
36
31
|
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
|
|
37
32
|
/** Default for -permitbaremultisig */
|
|
@@ -46,12 +41,6 @@ static const unsigned int MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE = 80;
|
|
|
46
41
|
static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
|
|
47
42
|
/** The maximum size of a standard ScriptSig */
|
|
48
43
|
static const unsigned int MAX_STANDARD_SCRIPTSIG_SIZE = 1650;
|
|
49
|
-
/** Min feerate for defining dust. Historically this has been based on the
|
|
50
|
-
* minRelayTxFee, however changing the dust limit changes which transactions are
|
|
51
|
-
* standard and should be done with care and ideally rarely. It makes sense to
|
|
52
|
-
* only increase the dust limit after prior releases were already not creating
|
|
53
|
-
* outputs below the new threshold */
|
|
54
|
-
static const unsigned int DUST_RELAY_TX_FEE = 3000;
|
|
55
44
|
/**
|
|
56
45
|
* Standard script verification flags that standard transactions will comply
|
|
57
46
|
* with. However scripts violating these flags may still be present in valid
|
|
@@ -73,7 +62,6 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE
|
|
|
73
62
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
|
|
74
63
|
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE |
|
|
75
64
|
SCRIPT_VERIFY_CONST_SCRIPTCODE |
|
|
76
|
-
SCRIPT_VERIFY_TAPROOT |
|
|
77
65
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION |
|
|
78
66
|
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS |
|
|
79
67
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE;
|
|
@@ -85,23 +73,19 @@ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCR
|
|
|
85
73
|
static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
|
|
86
74
|
LOCKTIME_MEDIAN_TIME_PAST;
|
|
87
75
|
|
|
88
|
-
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee);
|
|
89
|
-
|
|
90
|
-
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee);
|
|
91
|
-
|
|
92
76
|
bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType);
|
|
93
77
|
|
|
94
78
|
|
|
95
79
|
// Changing the default transaction version requires a two step process: first
|
|
96
80
|
// adapting relay policy by bumping TX_MAX_STANDARD_VERSION, and then later
|
|
97
81
|
// allowing the new transaction version in the wallet/RPC.
|
|
98
|
-
static constexpr decltype(CTransaction::nVersion) TX_MAX_STANDARD_VERSION{
|
|
82
|
+
static constexpr decltype(CTransaction::nVersion) TX_MAX_STANDARD_VERSION{3};
|
|
99
83
|
|
|
100
84
|
/**
|
|
101
85
|
* Check for standard transaction types
|
|
102
86
|
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
|
|
103
87
|
*/
|
|
104
|
-
bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig,
|
|
88
|
+
bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std::string& reason);
|
|
105
89
|
/**
|
|
106
90
|
* Check for standard transaction types
|
|
107
91
|
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2016-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#include <policy/rbf.h>
|
|
6
|
-
|
|
7
|
-
#include <policy/settings.h>
|
|
8
|
-
#include <tinyformat.h>
|
|
9
|
-
#include <util/moneystr.h>
|
|
10
|
-
#include <util/rbf.h>
|
|
11
|
-
|
|
12
|
-
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
|
|
13
|
-
{
|
|
14
|
-
AssertLockHeld(pool.cs);
|
|
15
|
-
|
|
16
|
-
CTxMemPool::setEntries ancestors;
|
|
17
|
-
|
|
18
|
-
// First check the transaction itself.
|
|
19
|
-
if (SignalsOptInRBF(tx)) {
|
|
20
|
-
return RBFTransactionState::REPLACEABLE_BIP125;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// If this transaction is not in our mempool, then we can't be sure
|
|
24
|
-
// we will know about all its inputs.
|
|
25
|
-
if (!pool.exists(GenTxid::Txid(tx.GetHash()))) {
|
|
26
|
-
return RBFTransactionState::UNKNOWN;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// If all the inputs have nSequence >= maxint-1, it still might be
|
|
30
|
-
// signaled for RBF if any unconfirmed parents have signaled.
|
|
31
|
-
uint64_t noLimit = std::numeric_limits<uint64_t>::max();
|
|
32
|
-
std::string dummy;
|
|
33
|
-
CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash());
|
|
34
|
-
pool.CalculateMemPoolAncestors(entry, ancestors, noLimit, noLimit, noLimit, noLimit, dummy, false);
|
|
35
|
-
|
|
36
|
-
for (CTxMemPool::txiter it : ancestors) {
|
|
37
|
-
if (SignalsOptInRBF(it->GetTx())) {
|
|
38
|
-
return RBFTransactionState::REPLACEABLE_BIP125;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return RBFTransactionState::FINAL;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
|
|
45
|
-
{
|
|
46
|
-
// If we don't have a local mempool we can only check the transaction itself.
|
|
47
|
-
return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
|
|
51
|
-
CTxMemPool& pool,
|
|
52
|
-
const CTxMemPool::setEntries& iters_conflicting,
|
|
53
|
-
CTxMemPool::setEntries& all_conflicts)
|
|
54
|
-
{
|
|
55
|
-
AssertLockHeld(pool.cs);
|
|
56
|
-
const uint256 txid = tx.GetHash();
|
|
57
|
-
uint64_t nConflictingCount = 0;
|
|
58
|
-
for (const auto& mi : iters_conflicting) {
|
|
59
|
-
nConflictingCount += mi->GetCountWithDescendants();
|
|
60
|
-
// BIP125 Rule #5: don't consider replacing more than MAX_BIP125_REPLACEMENT_CANDIDATES
|
|
61
|
-
// entries from the mempool. This potentially overestimates the number of actual
|
|
62
|
-
// descendants (i.e. if multiple conflicts share a descendant, it will be counted multiple
|
|
63
|
-
// times), but we just want to be conservative to avoid doing too much work.
|
|
64
|
-
if (nConflictingCount > MAX_BIP125_REPLACEMENT_CANDIDATES) {
|
|
65
|
-
return strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
|
|
66
|
-
txid.ToString(),
|
|
67
|
-
nConflictingCount,
|
|
68
|
-
MAX_BIP125_REPLACEMENT_CANDIDATES);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// Calculate the set of all transactions that would have to be evicted.
|
|
72
|
-
for (CTxMemPool::txiter it : iters_conflicting) {
|
|
73
|
-
pool.CalculateDescendants(it, all_conflicts);
|
|
74
|
-
}
|
|
75
|
-
return std::nullopt;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
|
|
79
|
-
const CTxMemPool& pool,
|
|
80
|
-
const CTxMemPool::setEntries& iters_conflicting)
|
|
81
|
-
{
|
|
82
|
-
AssertLockHeld(pool.cs);
|
|
83
|
-
std::set<uint256> parents_of_conflicts;
|
|
84
|
-
for (const auto& mi : iters_conflicting) {
|
|
85
|
-
for (const CTxIn& txin : mi->GetTx().vin) {
|
|
86
|
-
parents_of_conflicts.insert(txin.prevout.hash);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
for (unsigned int j = 0; j < tx.vin.size(); j++) {
|
|
91
|
-
// BIP125 Rule #2: We don't want to accept replacements that require low feerate junk to be
|
|
92
|
-
// mined first. Ideally we'd keep track of the ancestor feerates and make the decision
|
|
93
|
-
// based on that, but for now requiring all new inputs to be confirmed works.
|
|
94
|
-
//
|
|
95
|
-
// Note that if you relax this to make RBF a little more useful, this may break the
|
|
96
|
-
// CalculateMempoolAncestors RBF relaxation which subtracts the conflict count/size from the
|
|
97
|
-
// descendant limit.
|
|
98
|
-
if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) {
|
|
99
|
-
// Rather than check the UTXO set - potentially expensive - it's cheaper to just check
|
|
100
|
-
// if the new input refers to a tx that's in the mempool.
|
|
101
|
-
if (pool.exists(GenTxid::Txid(tx.vin[j].prevout.hash))) {
|
|
102
|
-
return strprintf("replacement %s adds unconfirmed input, idx %d",
|
|
103
|
-
tx.GetHash().ToString(), j);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return std::nullopt;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
|
|
111
|
-
const std::set<uint256>& direct_conflicts,
|
|
112
|
-
const uint256& txid)
|
|
113
|
-
{
|
|
114
|
-
for (CTxMemPool::txiter ancestorIt : ancestors) {
|
|
115
|
-
const uint256& hashAncestor = ancestorIt->GetTx().GetHash();
|
|
116
|
-
if (direct_conflicts.count(hashAncestor)) {
|
|
117
|
-
return strprintf("%s spends conflicting transaction %s",
|
|
118
|
-
txid.ToString(),
|
|
119
|
-
hashAncestor.ToString());
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return std::nullopt;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting,
|
|
126
|
-
CFeeRate replacement_feerate,
|
|
127
|
-
const uint256& txid)
|
|
128
|
-
{
|
|
129
|
-
for (const auto& mi : iters_conflicting) {
|
|
130
|
-
// Don't allow the replacement to reduce the feerate of the mempool.
|
|
131
|
-
//
|
|
132
|
-
// We usually don't want to accept replacements with lower feerates than what they replaced
|
|
133
|
-
// as that would lower the feerate of the next block. Requiring that the feerate always be
|
|
134
|
-
// increased is also an easy-to-reason about way to prevent DoS attacks via replacements.
|
|
135
|
-
//
|
|
136
|
-
// We only consider the feerates of transactions being directly replaced, not their indirect
|
|
137
|
-
// descendants. While that does mean high feerate children are ignored when deciding whether
|
|
138
|
-
// or not to replace, we do require the replacement to pay more overall fees too, mitigating
|
|
139
|
-
// most cases.
|
|
140
|
-
CFeeRate original_feerate(mi->GetModifiedFee(), mi->GetTxSize());
|
|
141
|
-
if (replacement_feerate <= original_feerate) {
|
|
142
|
-
return strprintf("rejecting replacement %s; new feerate %s <= old feerate %s",
|
|
143
|
-
txid.ToString(),
|
|
144
|
-
replacement_feerate.ToString(),
|
|
145
|
-
original_feerate.ToString());
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return std::nullopt;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
std::optional<std::string> PaysForRBF(CAmount original_fees,
|
|
152
|
-
CAmount replacement_fees,
|
|
153
|
-
size_t replacement_vsize,
|
|
154
|
-
CFeeRate relay_fee,
|
|
155
|
-
const uint256& txid)
|
|
156
|
-
{
|
|
157
|
-
// BIP125 Rule #3: The replacement fees must be greater than or equal to fees of the
|
|
158
|
-
// transactions it replaces, otherwise the bandwidth used by those conflicting transactions
|
|
159
|
-
// would not be paid for.
|
|
160
|
-
if (replacement_fees < original_fees) {
|
|
161
|
-
return strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
|
|
162
|
-
txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// BIP125 Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS
|
|
166
|
-
// vector where attackers can cause a transaction to be replaced (and relayed) repeatedly by
|
|
167
|
-
// increasing the fee by tiny amounts.
|
|
168
|
-
CAmount additional_fees = replacement_fees - original_fees;
|
|
169
|
-
if (additional_fees < relay_fee.GetFee(replacement_vsize)) {
|
|
170
|
-
return strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
|
|
171
|
-
txid.ToString(),
|
|
172
|
-
FormatMoney(additional_fees),
|
|
173
|
-
FormatMoney(relay_fee.GetFee(replacement_vsize)));
|
|
174
|
-
}
|
|
175
|
-
return std::nullopt;
|
|
176
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2016-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#ifndef BITCOIN_POLICY_RBF_H
|
|
6
|
-
#define BITCOIN_POLICY_RBF_H
|
|
7
|
-
|
|
8
|
-
#include <primitives/transaction.h>
|
|
9
|
-
#include <txmempool.h>
|
|
10
|
-
#include <uint256.h>
|
|
11
|
-
|
|
12
|
-
#include <optional>
|
|
13
|
-
#include <string>
|
|
14
|
-
|
|
15
|
-
/** Maximum number of transactions that can be replaced by BIP125 RBF (Rule #5). This includes all
|
|
16
|
-
* mempool conflicts and their descendants. */
|
|
17
|
-
static constexpr uint32_t MAX_BIP125_REPLACEMENT_CANDIDATES{100};
|
|
18
|
-
|
|
19
|
-
/** The rbf state of unconfirmed transactions */
|
|
20
|
-
enum class RBFTransactionState {
|
|
21
|
-
/** Unconfirmed tx that does not signal rbf and is not in the mempool */
|
|
22
|
-
UNKNOWN,
|
|
23
|
-
/** Either this tx or a mempool ancestor signals rbf */
|
|
24
|
-
REPLACEABLE_BIP125,
|
|
25
|
-
/** Neither this tx nor a mempool ancestor signals rbf */
|
|
26
|
-
FINAL,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Determine whether an unconfirmed transaction is signaling opt-in to RBF
|
|
31
|
-
* according to BIP 125
|
|
32
|
-
* This involves checking sequence numbers of the transaction, as well
|
|
33
|
-
* as the sequence numbers of all in-mempool ancestors.
|
|
34
|
-
*
|
|
35
|
-
* @param tx The unconfirmed transaction
|
|
36
|
-
* @param pool The mempool, which may contain the tx
|
|
37
|
-
*
|
|
38
|
-
* @return The rbf state
|
|
39
|
-
*/
|
|
40
|
-
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
|
|
41
|
-
RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx);
|
|
42
|
-
|
|
43
|
-
/** Get all descendants of iters_conflicting. Also enforce BIP125 Rule #5, "The number of original
|
|
44
|
-
* transactions to be replaced and their descendant transactions which will be evicted from the
|
|
45
|
-
* mempool must not exceed a total of 100 transactions." Quit as early as possible. There cannot be
|
|
46
|
-
* more than MAX_BIP125_REPLACEMENT_CANDIDATES potential entries.
|
|
47
|
-
* @param[in] iters_conflicting The set of iterators to mempool entries.
|
|
48
|
-
* @param[out] all_conflicts Populated with all the mempool entries that would be replaced,
|
|
49
|
-
* which includes descendants of iters_conflicting. Not cleared at
|
|
50
|
-
* the start; any existing mempool entries will remain in the set.
|
|
51
|
-
* @returns an error message if Rule #5 is broken, otherwise a std::nullopt.
|
|
52
|
-
*/
|
|
53
|
-
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx, CTxMemPool& pool,
|
|
54
|
-
const CTxMemPool::setEntries& iters_conflicting,
|
|
55
|
-
CTxMemPool::setEntries& all_conflicts)
|
|
56
|
-
EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
|
|
57
|
-
|
|
58
|
-
/** BIP125 Rule #2: "The replacement transaction may only include an unconfirmed input if that input
|
|
59
|
-
* was included in one of the original transactions."
|
|
60
|
-
* @returns error message if Rule #2 is broken, otherwise std::nullopt. */
|
|
61
|
-
std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx, const CTxMemPool& pool,
|
|
62
|
-
const CTxMemPool::setEntries& iters_conflicting)
|
|
63
|
-
EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
|
|
64
|
-
|
|
65
|
-
/** Check the intersection between two sets of transactions (a set of mempool entries and a set of
|
|
66
|
-
* txids) to make sure they are disjoint.
|
|
67
|
-
* @param[in] ancestors Set of mempool entries corresponding to ancestors of the
|
|
68
|
-
* replacement transactions.
|
|
69
|
-
* @param[in] direct_conflicts Set of txids corresponding to the mempool conflicts
|
|
70
|
-
* (candidates to be replaced).
|
|
71
|
-
* @param[in] txid Transaction ID, included in the error message if violation occurs.
|
|
72
|
-
* @returns error message if the sets intersect, std::nullopt if they are disjoint.
|
|
73
|
-
*/
|
|
74
|
-
std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
|
|
75
|
-
const std::set<uint256>& direct_conflicts,
|
|
76
|
-
const uint256& txid);
|
|
77
|
-
|
|
78
|
-
/** Check that the feerate of the replacement transaction(s) is higher than the feerate of each
|
|
79
|
-
* of the transactions in iters_conflicting.
|
|
80
|
-
* @param[in] iters_conflicting The set of mempool entries.
|
|
81
|
-
* @returns error message if fees insufficient, otherwise std::nullopt.
|
|
82
|
-
*/
|
|
83
|
-
std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting,
|
|
84
|
-
CFeeRate replacement_feerate, const uint256& txid);
|
|
85
|
-
|
|
86
|
-
/** Enforce BIP125 Rule #3 "The replacement transaction pays an absolute fee of at least the sum
|
|
87
|
-
* paid by the original transactions." Enforce BIP125 Rule #4 "The replacement transaction must also
|
|
88
|
-
* pay for its own bandwidth at or above the rate set by the node's minimum relay fee setting."
|
|
89
|
-
* @param[in] original_fees Total modified fees of original transaction(s).
|
|
90
|
-
* @param[in] replacement_fees Total modified fees of replacement transaction(s).
|
|
91
|
-
* @param[in] replacement_vsize Total virtual size of replacement transaction(s).
|
|
92
|
-
* @param[in] relay_fee The node's minimum feerate for transaction relay.
|
|
93
|
-
* @param[in] txid Transaction ID, included in the error message if violation occurs.
|
|
94
|
-
* @returns error string if fees are insufficient, otherwise std::nullopt.
|
|
95
|
-
*/
|
|
96
|
-
std::optional<std::string> PaysForRBF(CAmount original_fees,
|
|
97
|
-
CAmount replacement_fees,
|
|
98
|
-
size_t replacement_vsize,
|
|
99
|
-
CFeeRate relay_fee,
|
|
100
|
-
const uint256& txid);
|
|
101
|
-
|
|
102
|
-
#endif // BITCOIN_POLICY_RBF_H
|
|
@@ -5,10 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
#include <policy/settings.h>
|
|
7
7
|
|
|
8
|
-
#include <policy/feerate.h>
|
|
9
8
|
#include <policy/policy.h>
|
|
10
9
|
|
|
11
10
|
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
|
|
12
|
-
CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
|
|
13
|
-
CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
|
|
14
11
|
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
|
|
@@ -8,18 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
#include <policy/policy.h>
|
|
10
10
|
|
|
11
|
-
class CFeeRate;
|
|
12
11
|
class CTransaction;
|
|
13
12
|
|
|
14
13
|
// Policy settings which are configurable at runtime.
|
|
15
|
-
extern CFeeRate incrementalRelayFee;
|
|
16
|
-
extern CFeeRate dustRelayFee;
|
|
17
14
|
extern unsigned int nBytesPerSigOp;
|
|
18
15
|
extern bool fIsBareMultisigStd;
|
|
19
16
|
|
|
20
17
|
static inline bool IsStandardTx(const CTransaction& tx, std::string& reason)
|
|
21
18
|
{
|
|
22
|
-
return IsStandardTx(tx, ::fIsBareMultisigStd,
|
|
19
|
+
return IsStandardTx(tx, ::fIsBareMultisigStd, reason);
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
static inline int64_t GetVirtualTransactionSize(int64_t weight, int64_t sigop_cost)
|
|
@@ -10,63 +10,53 @@
|
|
|
10
10
|
#include <primitives/block.h>
|
|
11
11
|
#include <uint256.h>
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
#include <bignum.h>
|
|
14
|
+
#include <chainparams.h>
|
|
15
|
+
#include <kernel.h>
|
|
16
|
+
|
|
17
|
+
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake, const Consensus::Params& params)
|
|
14
18
|
{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
19
|
+
if (pindexLast == nullptr)
|
|
20
|
+
return UintToArith256(params.powLimit).GetCompact(); // genesis block
|
|
21
|
+
|
|
22
|
+
const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
|
|
23
|
+
if (pindexPrev->pprev == nullptr)
|
|
24
|
+
return UintToArith256(params.bnInitialHashTarget).GetCompact(); // first block
|
|
25
|
+
const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake);
|
|
26
|
+
if (pindexPrevPrev->pprev == nullptr)
|
|
27
|
+
return UintToArith256(params.bnInitialHashTarget).GetCompact(); // second block
|
|
28
|
+
|
|
29
|
+
int64_t nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();
|
|
30
|
+
|
|
31
|
+
// rfc20
|
|
32
|
+
int64_t nHypotheticalSpacing = pindexLast->GetBlockTime() - pindexPrev->GetBlockTime();
|
|
33
|
+
if (!fProofOfStake && IsProtocolV12(pindexPrev) && (nHypotheticalSpacing > nActualSpacing))
|
|
34
|
+
nActualSpacing = nHypotheticalSpacing;
|
|
35
|
+
|
|
36
|
+
// peercoin: target change every block
|
|
37
|
+
// peercoin: retarget with exponential moving toward target spacing
|
|
38
|
+
CBigNum bnNew;
|
|
39
|
+
bnNew.SetCompact(pindexPrev->nBits);
|
|
40
|
+
if (Params().NetworkIDString() != CBaseChainParams::REGTEST) {
|
|
41
|
+
int64_t nTargetSpacing;
|
|
42
|
+
|
|
43
|
+
if (fProofOfStake) {
|
|
44
|
+
nTargetSpacing = params.nStakeTargetSpacing;
|
|
45
|
+
} else {
|
|
46
|
+
if (IsProtocolV09(pindexLast->nTime)) {
|
|
47
|
+
nTargetSpacing = params.nStakeTargetSpacing * 6;
|
|
48
|
+
} else {
|
|
49
|
+
nTargetSpacing = std::min(params.nTargetSpacingWorkMax, params.nStakeTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight));
|
|
35
50
|
}
|
|
36
51
|
}
|
|
37
|
-
return pindexLast->nBits;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Go back by what we want to be 14 days worth of blocks
|
|
41
|
-
int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);
|
|
42
|
-
assert(nHeightFirst >= 0);
|
|
43
|
-
const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst);
|
|
44
|
-
assert(pindexFirst);
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
int64_t nInterval = params.nTargetTimespan / nTargetSpacing;
|
|
54
|
+
bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
|
|
55
|
+
bnNew /= ((nInterval + 1) * nTargetSpacing);
|
|
56
|
+
}
|
|
48
57
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (params.fPowNoRetargeting)
|
|
52
|
-
return pindexLast->nBits;
|
|
53
|
-
|
|
54
|
-
// Limit adjustment step
|
|
55
|
-
int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
|
|
56
|
-
if (nActualTimespan < params.nPowTargetTimespan/4)
|
|
57
|
-
nActualTimespan = params.nPowTargetTimespan/4;
|
|
58
|
-
if (nActualTimespan > params.nPowTargetTimespan*4)
|
|
59
|
-
nActualTimespan = params.nPowTargetTimespan*4;
|
|
60
|
-
|
|
61
|
-
// Retarget
|
|
62
|
-
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
|
|
63
|
-
arith_uint256 bnNew;
|
|
64
|
-
bnNew.SetCompact(pindexLast->nBits);
|
|
65
|
-
bnNew *= nActualTimespan;
|
|
66
|
-
bnNew /= params.nPowTargetTimespan;
|
|
67
|
-
|
|
68
|
-
if (bnNew > bnPowLimit)
|
|
69
|
-
bnNew = bnPowLimit;
|
|
58
|
+
if (bnNew > CBigNum(params.powLimit))
|
|
59
|
+
bnNew = CBigNum(params.powLimit);
|
|
70
60
|
|
|
71
61
|
return bnNew.GetCompact();
|
|
72
62
|
}
|
|
@@ -14,8 +14,7 @@ class CBlockHeader;
|
|
|
14
14
|
class CBlockIndex;
|
|
15
15
|
class uint256;
|
|
16
16
|
|
|
17
|
-
unsigned int
|
|
18
|
-
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params&);
|
|
17
|
+
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake, const Consensus::Params& params);
|
|
19
18
|
|
|
20
19
|
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
|
|
21
20
|
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&);
|
|
@@ -10,19 +10,21 @@
|
|
|
10
10
|
|
|
11
11
|
uint256 CBlockHeader::GetHash() const
|
|
12
12
|
{
|
|
13
|
-
|
|
13
|
+
CBlockHeader tmp(*this);
|
|
14
|
+
tmp.nFlags = 0;
|
|
15
|
+
return SerializeHash(tmp);
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
std::string CBlock::ToString() const
|
|
17
19
|
{
|
|
18
20
|
std::stringstream s;
|
|
19
|
-
s << strprintf("CBlock(hash=%s, ver=0x%08x, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n",
|
|
21
|
+
s << strprintf("CBlock(hash=%s, ver=0x%08x, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, nFlags=%08x, vtx=%u)\n",
|
|
20
22
|
GetHash().ToString(),
|
|
21
23
|
nVersion,
|
|
22
24
|
hashPrevBlock.ToString(),
|
|
23
25
|
hashMerkleRoot.ToString(),
|
|
24
26
|
nTime, nBits, nNonce,
|
|
25
|
-
vtx.size());
|
|
27
|
+
nFlags, vtx.size());
|
|
26
28
|
for (const auto& tx : vtx) {
|
|
27
29
|
s << " " << tx->ToString() << "\n";
|
|
28
30
|
}
|
|
@@ -28,21 +28,34 @@ public:
|
|
|
28
28
|
uint32_t nBits;
|
|
29
29
|
uint32_t nNonce;
|
|
30
30
|
|
|
31
|
+
// peercoin: A copy from CBlockIndex.nFlags from other clients. We need this information because we are using headers-first syncronization.
|
|
32
|
+
uint32_t nFlags;
|
|
33
|
+
// peercoin: Used in CheckProofOfStake().
|
|
34
|
+
static const int32_t NORMAL_SERIALIZE_SIZE=80;
|
|
35
|
+
static const int32_t CURRENT_VERSION=4;
|
|
36
|
+
|
|
31
37
|
CBlockHeader()
|
|
32
38
|
{
|
|
33
39
|
SetNull();
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
SERIALIZE_METHODS(CBlockHeader, obj)
|
|
42
|
+
SERIALIZE_METHODS(CBlockHeader, obj)
|
|
43
|
+
{
|
|
44
|
+
READWRITE(obj.nVersion, obj.hashPrevBlock, obj.hashMerkleRoot, obj.nTime, obj.nBits, obj.nNonce);
|
|
45
|
+
// peercoin: do not serialize nFlags when computing hash
|
|
46
|
+
if (!(s.GetType() & SER_GETHASH) && s.GetType() & SER_POSMARKER)
|
|
47
|
+
READWRITE(obj.nFlags);
|
|
48
|
+
}
|
|
37
49
|
|
|
38
50
|
void SetNull()
|
|
39
51
|
{
|
|
40
|
-
nVersion =
|
|
52
|
+
nVersion = CURRENT_VERSION;
|
|
41
53
|
hashPrevBlock.SetNull();
|
|
42
54
|
hashMerkleRoot.SetNull();
|
|
43
55
|
nTime = 0;
|
|
44
56
|
nBits = 0;
|
|
45
57
|
nNonce = 0;
|
|
58
|
+
nFlags = 0;
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
bool IsNull() const
|
|
@@ -65,6 +78,9 @@ public:
|
|
|
65
78
|
// network and disk
|
|
66
79
|
std::vector<CTransactionRef> vtx;
|
|
67
80
|
|
|
81
|
+
// peercoin: block signature - signed by coin base txout[0]'s owner
|
|
82
|
+
std::vector<unsigned char> vchBlockSig;
|
|
83
|
+
|
|
68
84
|
// memory only
|
|
69
85
|
mutable bool fChecked;
|
|
70
86
|
|
|
@@ -83,6 +99,7 @@ public:
|
|
|
83
99
|
{
|
|
84
100
|
READWRITEAS(CBlockHeader, obj);
|
|
85
101
|
READWRITE(obj.vtx);
|
|
102
|
+
READWRITE(obj.vchBlockSig);
|
|
86
103
|
}
|
|
87
104
|
|
|
88
105
|
void SetNull()
|
|
@@ -90,6 +107,7 @@ public:
|
|
|
90
107
|
CBlockHeader::SetNull();
|
|
91
108
|
vtx.clear();
|
|
92
109
|
fChecked = false;
|
|
110
|
+
vchBlockSig.clear();
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
CBlockHeader GetBlockHeader() const
|
|
@@ -101,9 +119,37 @@ public:
|
|
|
101
119
|
block.nTime = nTime;
|
|
102
120
|
block.nBits = nBits;
|
|
103
121
|
block.nNonce = nNonce;
|
|
122
|
+
block.nFlags = nFlags;
|
|
104
123
|
return block;
|
|
105
124
|
}
|
|
106
125
|
|
|
126
|
+
// peercoin: two types of block: proof-of-work or proof-of-stake
|
|
127
|
+
bool IsProofOfStake() const
|
|
128
|
+
{
|
|
129
|
+
return (vtx.size() > 1 && vtx[1]->IsCoinStake());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
bool IsProofOfWork() const
|
|
133
|
+
{
|
|
134
|
+
return !IsProofOfStake();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
std::pair<COutPoint, unsigned int> GetProofOfStake() const
|
|
138
|
+
{
|
|
139
|
+
return IsProofOfStake() ? std::make_pair(vtx[1]->vin[0].prevout, vtx[1]->nTime) : std::make_pair(COutPoint(), (unsigned int)0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// peercoin: get max transaction timestamp
|
|
143
|
+
int64_t GetMaxTransactionTime() const
|
|
144
|
+
{
|
|
145
|
+
int64_t maxTransactionTime = 0;
|
|
146
|
+
for (const auto& tx : vtx)
|
|
147
|
+
maxTransactionTime = std::max(maxTransactionTime, (int64_t)tx->nTime);
|
|
148
|
+
return maxTransactionTime;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
unsigned int GetStakeEntropyBit() const; // peercoin: entropy bit for stake modifier if chosen by modifier
|
|
152
|
+
|
|
107
153
|
std::string ToString() const;
|
|
108
154
|
};
|
|
109
155
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include <util/strencodings.h>
|
|
12
12
|
|
|
13
13
|
#include <assert.h>
|
|
14
|
+
#include <timedata.h>
|
|
14
15
|
|
|
15
16
|
std::string COutPoint::ToString() const
|
|
16
17
|
{
|
|
@@ -54,11 +55,11 @@ CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn)
|
|
|
54
55
|
|
|
55
56
|
std::string CTxOut::ToString() const
|
|
56
57
|
{
|
|
57
|
-
return strprintf("CTxOut(nValue=%d.%
|
|
58
|
+
return strprintf("CTxOut(nValue=%d.%06d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
|
|
61
|
-
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime) {}
|
|
61
|
+
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nTime(GetAdjustedTime()), nLockTime(0) {}
|
|
62
|
+
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nTime(tx.nTime), nLockTime(tx.nLockTime) {}
|
|
62
63
|
|
|
63
64
|
uint256 CMutableTransaction::GetHash() const
|
|
64
65
|
{
|
|
@@ -78,8 +79,8 @@ uint256 CTransaction::ComputeWitnessHash() const
|
|
|
78
79
|
return SerializeHash(*this, SER_GETHASH, 0);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {}
|
|
82
|
-
CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {}
|
|
82
|
+
CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nTime(tx.nTime), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {}
|
|
83
|
+
CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nTime(tx.nTime), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {}
|
|
83
84
|
|
|
84
85
|
CAmount CTransaction::GetValueOut() const
|
|
85
86
|
{
|
|
@@ -101,8 +102,10 @@ unsigned int CTransaction::GetTotalSize() const
|
|
|
101
102
|
std::string CTransaction::ToString() const
|
|
102
103
|
{
|
|
103
104
|
std::string str;
|
|
104
|
-
str +=
|
|
105
|
+
str += IsCoinBase()? "Coinbase" : (IsCoinStake()? "Coinstake" : "CTransaction");
|
|
106
|
+
str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n",
|
|
105
107
|
GetHash().ToString().substr(0,10),
|
|
108
|
+
nTime,
|
|
106
109
|
nVersion,
|
|
107
110
|
vin.size(),
|
|
108
111
|
vout.size(),
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#include <serialize.h>
|
|
13
13
|
#include <uint256.h>
|
|
14
14
|
|
|
15
|
+
#include <version.h>
|
|
15
16
|
#include <tuple>
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -171,6 +172,17 @@ public:
|
|
|
171
172
|
return (nValue == -1);
|
|
172
173
|
}
|
|
173
174
|
|
|
175
|
+
void SetEmpty()
|
|
176
|
+
{
|
|
177
|
+
nValue = 0;
|
|
178
|
+
scriptPubKey.clear();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
bool IsEmpty() const
|
|
182
|
+
{
|
|
183
|
+
return (nValue == 0 && scriptPubKey.empty());
|
|
184
|
+
}
|
|
185
|
+
|
|
174
186
|
friend bool operator==(const CTxOut& a, const CTxOut& b)
|
|
175
187
|
{
|
|
176
188
|
return (a.nValue == b.nValue &&
|
|
@@ -209,6 +221,11 @@ inline void UnserializeTransaction(TxType& tx, Stream& s) {
|
|
|
209
221
|
const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);
|
|
210
222
|
|
|
211
223
|
s >> tx.nVersion;
|
|
224
|
+
if (tx.nVersion < 3)
|
|
225
|
+
s >> tx.nTime;
|
|
226
|
+
else
|
|
227
|
+
tx.nTime = 0;
|
|
228
|
+
|
|
212
229
|
unsigned char flags = 0;
|
|
213
230
|
tx.vin.clear();
|
|
214
231
|
tx.vout.clear();
|
|
@@ -248,6 +265,8 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) {
|
|
|
248
265
|
const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);
|
|
249
266
|
|
|
250
267
|
s << tx.nVersion;
|
|
268
|
+
if (tx.nVersion<3)
|
|
269
|
+
s << tx.nTime;
|
|
251
270
|
unsigned char flags = 0;
|
|
252
271
|
// Consistency check
|
|
253
272
|
if (fAllowWitness) {
|
|
@@ -272,7 +291,6 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) {
|
|
|
272
291
|
s << tx.nLockTime;
|
|
273
292
|
}
|
|
274
293
|
|
|
275
|
-
|
|
276
294
|
/** The basic transaction that is broadcasted on the network and contained in
|
|
277
295
|
* blocks. A transaction can contain multiple inputs and outputs.
|
|
278
296
|
*/
|
|
@@ -280,7 +298,7 @@ class CTransaction
|
|
|
280
298
|
{
|
|
281
299
|
public:
|
|
282
300
|
// Default transaction version.
|
|
283
|
-
static const int32_t CURRENT_VERSION=
|
|
301
|
+
static const int32_t CURRENT_VERSION=3;
|
|
284
302
|
|
|
285
303
|
// The local variables are made const to prevent unintended modification
|
|
286
304
|
// without updating the cached hash value. However, CTransaction is not
|
|
@@ -290,6 +308,7 @@ public:
|
|
|
290
308
|
const std::vector<CTxIn> vin;
|
|
291
309
|
const std::vector<CTxOut> vout;
|
|
292
310
|
const int32_t nVersion;
|
|
311
|
+
const uint32_t nTime;
|
|
293
312
|
const uint32_t nLockTime;
|
|
294
313
|
|
|
295
314
|
private:
|
|
@@ -334,7 +353,13 @@ public:
|
|
|
334
353
|
|
|
335
354
|
bool IsCoinBase() const
|
|
336
355
|
{
|
|
337
|
-
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
|
356
|
+
return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() >= 1);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
bool IsCoinStake() const
|
|
360
|
+
{
|
|
361
|
+
// peercoin: the coin stake transaction is marked with the first output empty
|
|
362
|
+
return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty());
|
|
338
363
|
}
|
|
339
364
|
|
|
340
365
|
friend bool operator==(const CTransaction& a, const CTransaction& b)
|
|
@@ -366,6 +391,7 @@ struct CMutableTransaction
|
|
|
366
391
|
std::vector<CTxIn> vin;
|
|
367
392
|
std::vector<CTxOut> vout;
|
|
368
393
|
int32_t nVersion;
|
|
394
|
+
uint32_t nTime;
|
|
369
395
|
uint32_t nLockTime;
|
|
370
396
|
|
|
371
397
|
CMutableTransaction();
|
|
@@ -181,6 +181,7 @@ const std::vector<std::string> &getAllNetMessageTypes()
|
|
|
181
181
|
return allNetMessageTypesVec;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
const unsigned int POW_HEADER_COOLING = 70;
|
|
184
185
|
/**
|
|
185
186
|
* Convert a service flag (NODE_*) to a human readable string.
|
|
186
187
|
* It supports unknown service flags which will be returned as "UNKNOWN[...]".
|
|
@@ -273,7 +273,7 @@ enum ServiceFlags : uint64_t {
|
|
|
273
273
|
// Nothing
|
|
274
274
|
NODE_NONE = 0,
|
|
275
275
|
// NODE_NETWORK means that the node is capable of serving the complete block chain. It is currently
|
|
276
|
-
// set by all Bitcoin Core
|
|
276
|
+
// set by all Bitcoin Core nodes, and is unset by SPV clients or other light clients.
|
|
277
277
|
NODE_NETWORK = (1 << 0),
|
|
278
278
|
// NODE_BLOOM means the node is capable and willing to handle bloom-filtered connections.
|
|
279
279
|
// Bitcoin Core nodes used to support this by default, without advertising this bit,
|
|
@@ -511,4 +511,7 @@ public:
|
|
|
511
511
|
/** Convert a TX/WITNESS_TX/WTX CInv to a GenTxid. */
|
|
512
512
|
GenTxid ToGenTxid(const CInv& inv);
|
|
513
513
|
|
|
514
|
+
/** peercoin: How much temperature a PoW header will remove */
|
|
515
|
+
extern const unsigned int POW_HEADER_COOLING;
|
|
516
|
+
|
|
514
517
|
#endif // BITCOIN_PROTOCOL_H
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
#include <attributes.h>
|
|
9
9
|
#include <node/transaction.h>
|
|
10
|
-
#include <policy/feerate.h>
|
|
11
10
|
#include <primitives/transaction.h>
|
|
12
11
|
#include <pubkey.h>
|
|
13
12
|
#include <script/keyorigin.h>
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
#include <univalue.h>
|
|
34
34
|
|
|
35
35
|
using node::GetTransaction;
|
|
36
|
-
using node::IsBlockPruned;
|
|
37
36
|
using node::NodeContext;
|
|
38
37
|
using node::ReadBlockFromDisk;
|
|
39
38
|
|
|
@@ -296,8 +295,6 @@ static bool rest_block(const std::any& context,
|
|
|
296
295
|
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
|
|
297
296
|
}
|
|
298
297
|
|
|
299
|
-
if (IsBlockPruned(pblockindex))
|
|
300
|
-
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
|
|
301
298
|
|
|
302
299
|
if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
|
|
303
300
|
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
#include <consensus/validation.h>
|
|
15
15
|
#include <core_io.h>
|
|
16
16
|
#include <deploymentinfo.h>
|
|
17
|
-
#include <deploymentstatus.h>
|
|
18
17
|
#include <fs.h>
|
|
19
18
|
#include <hash.h>
|
|
20
19
|
#include <index/blockfilterindex.h>
|
|
@@ -26,10 +25,7 @@
|
|
|
26
25
|
#include <node/coinstats.h>
|
|
27
26
|
#include <node/context.h>
|
|
28
27
|
#include <node/utxo_snapshot.h>
|
|
29
|
-
#include <policy/feerate.h>
|
|
30
|
-
#include <policy/fees.h>
|
|
31
28
|
#include <policy/policy.h>
|
|
32
|
-
#include <policy/rbf.h>
|
|
33
29
|
#include <primitives/transaction.h>
|
|
34
30
|
#include <rpc/server.h>
|
|
35
31
|
#include <rpc/server_util.h>
|
|
@@ -45,13 +41,15 @@
|
|
|
45
41
|
#include <util/translation.h>
|
|
46
42
|
#include <validation.h>
|
|
47
43
|
#include <validationinterface.h>
|
|
48
|
-
#include <versionbits.h>
|
|
49
44
|
#include <warnings.h>
|
|
50
45
|
|
|
51
46
|
#include <stdint.h>
|
|
52
47
|
|
|
53
48
|
#include <univalue.h>
|
|
54
49
|
|
|
50
|
+
#include <node/miner.h>
|
|
51
|
+
#include <kernel.h>
|
|
52
|
+
#include <validation.h>
|
|
55
53
|
#include <condition_variable>
|
|
56
54
|
#include <memory>
|
|
57
55
|
#include <mutex>
|
|
@@ -60,7 +58,6 @@ using node::BlockManager;
|
|
|
60
58
|
using node::CCoinsStats;
|
|
61
59
|
using node::CoinStatsHashType;
|
|
62
60
|
using node::GetUTXOStats;
|
|
63
|
-
using node::IsBlockPruned;
|
|
64
61
|
using node::NodeContext;
|
|
65
62
|
using node::ReadBlockFromDisk;
|
|
66
63
|
using node::SnapshotMetadata;
|
|
@@ -78,21 +75,28 @@ static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange);
|
|
|
78
75
|
|
|
79
76
|
/* Calculate the difficulty for a given block index.
|
|
80
77
|
*/
|
|
81
|
-
double GetDifficulty(const CBlockIndex* blockindex)
|
|
78
|
+
double GetDifficulty(const CBlockIndex* blockindex, const CBlockIndex* tip)
|
|
82
79
|
{
|
|
83
|
-
|
|
80
|
+
LOCK(cs_main);
|
|
81
|
+
|
|
82
|
+
// minimum difficulty = 1.0.
|
|
83
|
+
if (blockindex == nullptr) {
|
|
84
|
+
if (tip == nullptr)
|
|
85
|
+
return 1.0;
|
|
86
|
+
else
|
|
87
|
+
blockindex = GetLastBlockIndex(tip, false);
|
|
88
|
+
}
|
|
84
89
|
|
|
85
90
|
int nShift = (blockindex->nBits >> 24) & 0xff;
|
|
91
|
+
|
|
86
92
|
double dDiff =
|
|
87
93
|
(double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff);
|
|
88
94
|
|
|
89
|
-
while (nShift < 29)
|
|
90
|
-
{
|
|
95
|
+
while (nShift < 29) {
|
|
91
96
|
dDiff *= 256.0;
|
|
92
97
|
nShift++;
|
|
93
98
|
}
|
|
94
|
-
while (nShift > 29)
|
|
95
|
-
{
|
|
99
|
+
while (nShift > 29) {
|
|
96
100
|
dDiff /= 256.0;
|
|
97
101
|
nShift--;
|
|
98
102
|
}
|
|
@@ -155,8 +159,8 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
|
|
|
155
159
|
result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
|
|
156
160
|
result.pushKV("nonce", (uint64_t)blockindex->nNonce);
|
|
157
161
|
result.pushKV("bits", strprintf("%08x", blockindex->nBits));
|
|
158
|
-
result.pushKV("difficulty", GetDifficulty(blockindex));
|
|
159
|
-
result.pushKV("chainwork", blockindex->
|
|
162
|
+
result.pushKV("difficulty", GetDifficulty(blockindex, tip));
|
|
163
|
+
result.pushKV("chainwork", blockindex->nChainTrust.GetHex());
|
|
160
164
|
result.pushKV("nTx", (uint64_t)blockindex->nTx);
|
|
161
165
|
|
|
162
166
|
if (blockindex->pprev)
|
|
@@ -173,6 +177,13 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
|
|
|
173
177
|
result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
|
|
174
178
|
result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
|
|
175
179
|
result.pushKV("weight", (int)::GetBlockWeight(block));
|
|
180
|
+
result.pushKV("mint", ValueFromAmount(blockindex->nMint));
|
|
181
|
+
result.pushKV("flags", strprintf("%s%s", blockindex->IsProofOfStake()? "proof-of-stake" : "proof-of-work", blockindex->GeneratedStakeModifier()? " stake-modifier": ""));
|
|
182
|
+
result.pushKV("proofhash", blockindex->IsProofOfStake()? blockindex->hashProofOfStake.GetHex() : blockindex->GetBlockHash().GetHex());
|
|
183
|
+
result.pushKV("entropybit", (int)blockindex->GetStakeEntropyBit());
|
|
184
|
+
result.pushKV("modifier", strprintf("%016llx", blockindex->nStakeModifier));
|
|
185
|
+
result.pushKV("modifierchecksum", strprintf("%08x", blockindex->nStakeModifierChecksum));
|
|
186
|
+
result.pushKV("blocksignature", HexStr(block.vchBlockSig));
|
|
176
187
|
UniValue txs(UniValue::VARR);
|
|
177
188
|
|
|
178
189
|
switch (verbosity) {
|
|
@@ -185,7 +196,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
|
|
|
185
196
|
case TxVerbosity::SHOW_DETAILS:
|
|
186
197
|
case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
|
|
187
198
|
CBlockUndo blockUndo;
|
|
188
|
-
const bool have_undo{WITH_LOCK(::cs_main, return
|
|
199
|
+
const bool have_undo{WITH_LOCK(::cs_main, return UndoReadFromDisk(blockUndo, blockindex))};
|
|
189
200
|
|
|
190
201
|
for (size_t i = 0; i < block.vtx.size(); ++i) {
|
|
191
202
|
const CTransactionRef& tx = block.vtx.at(i);
|
|
@@ -409,10 +420,14 @@ static RPCHelpMan syncwithvalidationinterfacequeue()
|
|
|
409
420
|
static RPCHelpMan getdifficulty()
|
|
410
421
|
{
|
|
411
422
|
return RPCHelpMan{"getdifficulty",
|
|
412
|
-
"\nReturns the
|
|
423
|
+
"\nReturns the difficulty as a multiple of the minimum difficulty.\n",
|
|
413
424
|
{},
|
|
414
425
|
RPCResult{
|
|
415
|
-
RPCResult::Type::
|
|
426
|
+
RPCResult::Type::OBJ, "", "",
|
|
427
|
+
{
|
|
428
|
+
{RPCResult::Type::NUM, "proof-of-work", "the difficulty as a multiple of the minimum difficulty of proof of work blocks"},
|
|
429
|
+
{RPCResult::Type::NUM, "proof-of-stake", "the difficulty as a multiple of the minimum difficulty of proof of stake blocks"},
|
|
430
|
+
}},
|
|
416
431
|
RPCExamples{
|
|
417
432
|
HelpExampleCli("getdifficulty", "")
|
|
418
433
|
+ HelpExampleRpc("getdifficulty", "")
|
|
@@ -421,7 +436,11 @@ static RPCHelpMan getdifficulty()
|
|
|
421
436
|
{
|
|
422
437
|
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
|
423
438
|
LOCK(cs_main);
|
|
424
|
-
|
|
439
|
+
UniValue obj(UniValue::VOBJ);
|
|
440
|
+
obj.pushKV("proof-of-work", GetDifficulty(NULL, chainman.ActiveChain().Tip()));
|
|
441
|
+
obj.pushKV("proof-of-stake", GetDifficulty(GetLastBlockIndex(chainman.ActiveChain().Tip(), true), chainman.ActiveChain().Tip()));
|
|
442
|
+
obj.pushKV("search-interval", (int)nLastCoinStakeSearchInterval);
|
|
443
|
+
return obj;
|
|
425
444
|
},
|
|
426
445
|
};
|
|
427
446
|
}
|
|
@@ -432,7 +451,7 @@ static std::vector<RPCResult> MempoolEntryDescription() { return {
|
|
|
432
451
|
RPCResult{RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true,
|
|
433
452
|
"transaction fee, denominated in " + CURRENCY_UNIT + " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
|
|
434
453
|
RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", /*optional=*/true,
|
|
435
|
-
"transaction fee with fee deltas used for mining priority, denominated in " +
|
|
454
|
+
"transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_ATOM +
|
|
436
455
|
" (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"},
|
|
437
456
|
RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"},
|
|
438
457
|
RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"},
|
|
@@ -458,7 +477,6 @@ static std::vector<RPCResult> MempoolEntryDescription() { return {
|
|
|
458
477
|
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}},
|
|
459
478
|
RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction",
|
|
460
479
|
{RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}},
|
|
461
|
-
RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction could be replaced due to BIP125 (replace-by-fee)"},
|
|
462
480
|
RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"},
|
|
463
481
|
};}
|
|
464
482
|
|
|
@@ -519,17 +537,6 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
|
|
|
519
537
|
}
|
|
520
538
|
|
|
521
539
|
info.pushKV("spentby", spent);
|
|
522
|
-
|
|
523
|
-
// Add opt-in RBF status
|
|
524
|
-
bool rbfStatus = false;
|
|
525
|
-
RBFTransactionState rbfState = IsRBFOptIn(tx, pool);
|
|
526
|
-
if (rbfState == RBFTransactionState::UNKNOWN) {
|
|
527
|
-
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool");
|
|
528
|
-
} else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) {
|
|
529
|
-
rbfStatus = true;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
info.pushKV("bip125-replaceable", rbfStatus);
|
|
533
540
|
info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
|
|
534
541
|
}
|
|
535
542
|
|
|
@@ -934,9 +941,6 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_RE
|
|
|
934
941
|
{
|
|
935
942
|
AssertLockHeld(::cs_main);
|
|
936
943
|
CBlock block;
|
|
937
|
-
if (IsBlockPruned(pblockindex)) {
|
|
938
|
-
throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
|
|
939
|
-
}
|
|
940
944
|
|
|
941
945
|
if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) {
|
|
942
946
|
// Block not found on disk. This could be because we have the block
|
|
@@ -952,9 +956,6 @@ static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS
|
|
|
952
956
|
{
|
|
953
957
|
AssertLockHeld(::cs_main);
|
|
954
958
|
CBlockUndo blockUndo;
|
|
955
|
-
if (IsBlockPruned(pblockindex)) {
|
|
956
|
-
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
|
|
957
|
-
}
|
|
958
959
|
|
|
959
960
|
if (!UndoReadFromDisk(blockUndo, pblockindex)) {
|
|
960
961
|
throw JSONRPCError(RPC_MISC_ERROR, "Can't read undo data from disk");
|
|
@@ -969,7 +970,7 @@ static RPCHelpMan getblock()
|
|
|
969
970
|
"\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
|
|
970
971
|
"If verbosity is 1, returns an Object with information about block <hash>.\n"
|
|
971
972
|
"If verbosity is 2, returns an Object with information about block <hash> and information about each transaction.\n"
|
|
972
|
-
"If verbosity is 3, returns an Object with information about block <hash> and information about each transaction, including prevout information for inputs
|
|
973
|
+
"If verbosity is 3, returns an Object with information about block <hash> and information about each transaction, including prevout information for inputs.\n",
|
|
973
974
|
{
|
|
974
975
|
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
|
|
975
976
|
{"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a JSON object, 2 for JSON object with transaction data, and 3 for JSON object with transaction data including prevout information for inputs"},
|
|
@@ -1101,65 +1102,6 @@ static RPCHelpMan getblock()
|
|
|
1101
1102
|
};
|
|
1102
1103
|
}
|
|
1103
1104
|
|
|
1104
|
-
static RPCHelpMan pruneblockchain()
|
|
1105
|
-
{
|
|
1106
|
-
return RPCHelpMan{"pruneblockchain", "",
|
|
1107
|
-
{
|
|
1108
|
-
{"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or to a " + UNIX_EPOCH_TIME + "\n"
|
|
1109
|
-
" to prune blocks whose block time is at least 2 hours older than the provided timestamp."},
|
|
1110
|
-
},
|
|
1111
|
-
RPCResult{
|
|
1112
|
-
RPCResult::Type::NUM, "", "Height of the last block pruned"},
|
|
1113
|
-
RPCExamples{
|
|
1114
|
-
HelpExampleCli("pruneblockchain", "1000")
|
|
1115
|
-
+ HelpExampleRpc("pruneblockchain", "1000")
|
|
1116
|
-
},
|
|
1117
|
-
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
1118
|
-
{
|
|
1119
|
-
if (!node::fPruneMode)
|
|
1120
|
-
throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
|
|
1121
|
-
|
|
1122
|
-
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
|
1123
|
-
LOCK(cs_main);
|
|
1124
|
-
CChainState& active_chainstate = chainman.ActiveChainstate();
|
|
1125
|
-
CChain& active_chain = active_chainstate.m_chain;
|
|
1126
|
-
|
|
1127
|
-
int heightParam = request.params[0].get_int();
|
|
1128
|
-
if (heightParam < 0)
|
|
1129
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height.");
|
|
1130
|
-
|
|
1131
|
-
// Height value more than a billion is too high to be a block height, and
|
|
1132
|
-
// too low to be a block time (corresponds to timestamp from Sep 2001).
|
|
1133
|
-
if (heightParam > 1000000000) {
|
|
1134
|
-
// Add a 2 hour buffer to include blocks which might have had old timestamps
|
|
1135
|
-
CBlockIndex* pindex = active_chain.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
|
|
1136
|
-
if (!pindex) {
|
|
1137
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
|
|
1138
|
-
}
|
|
1139
|
-
heightParam = pindex->nHeight;
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
unsigned int height = (unsigned int) heightParam;
|
|
1143
|
-
unsigned int chainHeight = (unsigned int) active_chain.Height();
|
|
1144
|
-
if (chainHeight < Params().PruneAfterHeight())
|
|
1145
|
-
throw JSONRPCError(RPC_MISC_ERROR, "Blockchain is too short for pruning.");
|
|
1146
|
-
else if (height > chainHeight)
|
|
1147
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height.");
|
|
1148
|
-
else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
|
|
1149
|
-
LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.\n");
|
|
1150
|
-
height = chainHeight - MIN_BLOCKS_TO_KEEP;
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
PruneBlockFilesManual(active_chainstate, height);
|
|
1154
|
-
const CBlockIndex* block = active_chain.Tip();
|
|
1155
|
-
CHECK_NONFATAL(block);
|
|
1156
|
-
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
|
|
1157
|
-
block = block->pprev;
|
|
1158
|
-
}
|
|
1159
|
-
return uint64_t(block->nHeight);
|
|
1160
|
-
},
|
|
1161
|
-
};
|
|
1162
|
-
}
|
|
1163
1105
|
|
|
1164
1106
|
CoinStatsHashType ParseHashType(const std::string& hash_type_input)
|
|
1165
1107
|
{
|
|
@@ -1337,7 +1279,7 @@ static RPCHelpMan gettxout()
|
|
|
1337
1279
|
{RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
|
|
1338
1280
|
{RPCResult::Type::STR_HEX, "hex", ""},
|
|
1339
1281
|
{RPCResult::Type::STR, "type", "The type, eg pubkeyhash"},
|
|
1340
|
-
{RPCResult::Type::STR, "address", /*optional=*/true, "The
|
|
1282
|
+
{RPCResult::Type::STR, "address", /*optional=*/true, "The Peercoin address (only if a well-defined address exists)"},
|
|
1341
1283
|
}},
|
|
1342
1284
|
{RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
|
|
1343
1285
|
}},
|
|
@@ -1430,96 +1372,37 @@ static RPCHelpMan verifychain()
|
|
|
1430
1372
|
};
|
|
1431
1373
|
}
|
|
1432
1374
|
|
|
1433
|
-
|
|
1375
|
+
/** Implementation of IsSuperMajority with better feedback */
|
|
1376
|
+
static UniValue SoftForkMajorityDesc(int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
|
1434
1377
|
{
|
|
1435
|
-
// For buried deployments.
|
|
1436
|
-
|
|
1437
|
-
if (!DeploymentEnabled(params, dep)) return;
|
|
1438
|
-
|
|
1439
1378
|
UniValue rv(UniValue::VOBJ);
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1379
|
+
bool activated = false;
|
|
1380
|
+
switch(version)
|
|
1381
|
+
{
|
|
1382
|
+
case 2:
|
|
1383
|
+
activated = pindex->nHeight >= consensusParams.BIP34Height;
|
|
1384
|
+
break;
|
|
1385
|
+
case 3:
|
|
1386
|
+
activated = IsBTC16BIPsEnabled(pindex->nTime);
|
|
1387
|
+
break;
|
|
1388
|
+
case 4:
|
|
1389
|
+
activated = IsProtocolV06(pindex);
|
|
1390
|
+
break;
|
|
1391
|
+
case 12:
|
|
1392
|
+
activated = IsProtocolV12(pindex);
|
|
1393
|
+
break;
|
|
1394
|
+
}
|
|
1395
|
+
rv.pushKV("status", activated);
|
|
1396
|
+
return rv;
|
|
1446
1397
|
}
|
|
1447
1398
|
|
|
1448
|
-
static
|
|
1399
|
+
static UniValue SoftForkDesc(const std::string &name, int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
|
1449
1400
|
{
|
|
1450
|
-
// For BIP9 deployments.
|
|
1451
|
-
|
|
1452
|
-
if (!DeploymentEnabled(consensusParams, id)) return;
|
|
1453
|
-
if (blockindex == nullptr) return;
|
|
1454
|
-
|
|
1455
|
-
auto get_state_name = [](const ThresholdState state) -> std::string {
|
|
1456
|
-
switch (state) {
|
|
1457
|
-
case ThresholdState::DEFINED: return "defined";
|
|
1458
|
-
case ThresholdState::STARTED: return "started";
|
|
1459
|
-
case ThresholdState::LOCKED_IN: return "locked_in";
|
|
1460
|
-
case ThresholdState::ACTIVE: return "active";
|
|
1461
|
-
case ThresholdState::FAILED: return "failed";
|
|
1462
|
-
}
|
|
1463
|
-
return "invalid";
|
|
1464
|
-
};
|
|
1465
|
-
|
|
1466
|
-
UniValue bip9(UniValue::VOBJ);
|
|
1467
|
-
|
|
1468
|
-
const ThresholdState next_state = g_versionbitscache.State(blockindex, consensusParams, id);
|
|
1469
|
-
const ThresholdState current_state = g_versionbitscache.State(blockindex->pprev, consensusParams, id);
|
|
1470
|
-
|
|
1471
|
-
const bool has_signal = (ThresholdState::STARTED == current_state || ThresholdState::LOCKED_IN == current_state);
|
|
1472
|
-
|
|
1473
|
-
// BIP9 parameters
|
|
1474
|
-
if (has_signal) {
|
|
1475
|
-
bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
|
|
1476
|
-
}
|
|
1477
|
-
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
|
|
1478
|
-
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
|
|
1479
|
-
bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
|
|
1480
|
-
|
|
1481
|
-
// BIP9 status
|
|
1482
|
-
bip9.pushKV("status", get_state_name(current_state));
|
|
1483
|
-
bip9.pushKV("since", g_versionbitscache.StateSinceHeight(blockindex->pprev, consensusParams, id));
|
|
1484
|
-
bip9.pushKV("status_next", get_state_name(next_state));
|
|
1485
|
-
|
|
1486
|
-
// BIP9 signalling status, if applicable
|
|
1487
|
-
if (has_signal) {
|
|
1488
|
-
UniValue statsUV(UniValue::VOBJ);
|
|
1489
|
-
std::vector<bool> signals;
|
|
1490
|
-
BIP9Stats statsStruct = g_versionbitscache.Statistics(blockindex, consensusParams, id, &signals);
|
|
1491
|
-
statsUV.pushKV("period", statsStruct.period);
|
|
1492
|
-
statsUV.pushKV("elapsed", statsStruct.elapsed);
|
|
1493
|
-
statsUV.pushKV("count", statsStruct.count);
|
|
1494
|
-
if (ThresholdState::LOCKED_IN != current_state) {
|
|
1495
|
-
statsUV.pushKV("threshold", statsStruct.threshold);
|
|
1496
|
-
statsUV.pushKV("possible", statsStruct.possible);
|
|
1497
|
-
}
|
|
1498
|
-
bip9.pushKV("statistics", statsUV);
|
|
1499
|
-
|
|
1500
|
-
std::string sig;
|
|
1501
|
-
sig.reserve(signals.size());
|
|
1502
|
-
for (const bool s : signals) {
|
|
1503
|
-
sig.push_back(s ? '#' : '-');
|
|
1504
|
-
}
|
|
1505
|
-
bip9.pushKV("signalling", sig);
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
1401
|
UniValue rv(UniValue::VOBJ);
|
|
1509
|
-
rv.pushKV("
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
rv.pushKV("active", ThresholdState::ACTIVE == next_state);
|
|
1514
|
-
rv.pushKV("bip9", bip9);
|
|
1515
|
-
|
|
1516
|
-
softforks.pushKV(DeploymentName(id), rv);
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
namespace {
|
|
1520
|
-
/* TODO: when -deprecatedrpc=softforks is removed, drop these */
|
|
1521
|
-
UniValue DeploymentInfo(const CBlockIndex* tip, const Consensus::Params& consensusParams);
|
|
1522
|
-
extern const std::vector<RPCResult> RPCHelpForDeployment;
|
|
1402
|
+
rv.pushKV("id", name);
|
|
1403
|
+
rv.pushKV("version", version);
|
|
1404
|
+
rv.pushKV("active", SoftForkMajorityDesc(version, pindex, consensusParams));
|
|
1405
|
+
return rv;
|
|
1523
1406
|
}
|
|
1524
1407
|
|
|
1525
1408
|
// used by rest.cpp:rest_chaininfo, so cannot be static
|
|
@@ -1543,16 +1426,6 @@ RPCHelpMan getblockchaininfo()
|
|
|
1543
1426
|
{RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"},
|
|
1544
1427
|
{RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
|
|
1545
1428
|
{RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"},
|
|
1546
|
-
{RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"},
|
|
1547
|
-
{RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "lowest-height complete block stored (only present if pruning is enabled)"},
|
|
1548
|
-
{RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
|
|
1549
|
-
{RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"},
|
|
1550
|
-
{RPCResult::Type::OBJ_DYN, "softforks", "(DEPRECATED, returned only if config option -deprecatedrpc=softforks is passed) status of softforks",
|
|
1551
|
-
{
|
|
1552
|
-
{RPCResult::Type::OBJ, "xxxx", "name of the softfork",
|
|
1553
|
-
RPCHelpForDeployment
|
|
1554
|
-
},
|
|
1555
|
-
}},
|
|
1556
1429
|
{RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
|
|
1557
1430
|
}},
|
|
1558
1431
|
RPCExamples{
|
|
@@ -1561,9 +1434,8 @@ RPCHelpMan getblockchaininfo()
|
|
|
1561
1434
|
},
|
|
1562
1435
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
1563
1436
|
{
|
|
1564
|
-
const ArgsManager& args{EnsureAnyArgsman(request.context)};
|
|
1565
|
-
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
|
1566
1437
|
LOCK(cs_main);
|
|
1438
|
+
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
|
1567
1439
|
CChainState& active_chainstate = chainman.ActiveChainstate();
|
|
1568
1440
|
|
|
1569
1441
|
const CBlockIndex* tip = active_chainstate.m_chain.Tip();
|
|
@@ -1574,35 +1446,22 @@ RPCHelpMan getblockchaininfo()
|
|
|
1574
1446
|
obj.pushKV("blocks", height);
|
|
1575
1447
|
obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1);
|
|
1576
1448
|
obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex());
|
|
1577
|
-
obj.pushKV("difficulty", (double)GetDifficulty(tip));
|
|
1449
|
+
obj.pushKV("difficulty", (double)GetDifficulty(tip, tip));
|
|
1578
1450
|
obj.pushKV("time", (int64_t)tip->nTime);
|
|
1579
1451
|
obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
|
|
1580
1452
|
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
|
|
1581
1453
|
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
|
|
1582
|
-
obj.pushKV("chainwork", tip->
|
|
1454
|
+
obj.pushKV("chainwork", tip->nChainTrust.GetHex());
|
|
1583
1455
|
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
|
|
1584
|
-
obj.pushKV("pruned", node::fPruneMode);
|
|
1585
|
-
if (node::fPruneMode) {
|
|
1586
|
-
const CBlockIndex* block = tip;
|
|
1587
|
-
CHECK_NONFATAL(block);
|
|
1588
|
-
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
|
|
1589
|
-
block = block->pprev;
|
|
1590
|
-
}
|
|
1591
1456
|
|
|
1592
|
-
|
|
1457
|
+
UniValue softforks(UniValue::VARR);
|
|
1458
|
+
softforks.push_back(SoftForkDesc("bip34", 2, tip, Params().GetConsensus()));
|
|
1459
|
+
softforks.push_back(SoftForkDesc("bip66", 3, tip, Params().GetConsensus()));
|
|
1460
|
+
softforks.push_back(SoftForkDesc("bip65", 4, tip, Params().GetConsensus()));
|
|
1461
|
+
softforks.push_back(SoftForkDesc("v12", 12, tip, Params().GetConsensus()));
|
|
1593
1462
|
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
obj.pushKV("automatic_pruning", automatic_pruning);
|
|
1597
|
-
if (automatic_pruning) {
|
|
1598
|
-
obj.pushKV("prune_target_size", node::nPruneTarget);
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
if (IsDeprecatedRPCEnabled("softforks")) {
|
|
1603
|
-
const Consensus::Params& consensusParams = Params().GetConsensus();
|
|
1604
|
-
obj.pushKV("softforks", DeploymentInfo(tip, consensusParams));
|
|
1605
|
-
}
|
|
1463
|
+
// BIP9SoftForkDescPushBack(tip, softforks, "taproot", consensusParams, Consensus::DEPLOYMENT_TAPROOT);
|
|
1464
|
+
obj.pushKV("softforks", softforks);
|
|
1606
1465
|
|
|
1607
1466
|
obj.pushKV("warnings", GetWarnings(false).original);
|
|
1608
1467
|
return obj;
|
|
@@ -1610,92 +1469,6 @@ RPCHelpMan getblockchaininfo()
|
|
|
1610
1469
|
};
|
|
1611
1470
|
}
|
|
1612
1471
|
|
|
1613
|
-
namespace {
|
|
1614
|
-
const std::vector<RPCResult> RPCHelpForDeployment{
|
|
1615
|
-
{RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
|
|
1616
|
-
{RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
|
|
1617
|
-
{RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
|
|
1618
|
-
{RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
|
|
1619
|
-
{
|
|
1620
|
-
{RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
|
|
1621
|
-
{RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
|
|
1622
|
-
{RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
|
|
1623
|
-
{RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
|
|
1624
|
-
{RPCResult::Type::STR, "status", "status of deployment at specified block (one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\")"},
|
|
1625
|
-
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
|
|
1626
|
-
{RPCResult::Type::STR, "status_next", "status of deployment at the next block"},
|
|
1627
|
-
{RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
|
|
1628
|
-
{
|
|
1629
|
-
{RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
|
|
1630
|
-
{RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
|
|
1631
|
-
{RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
|
|
1632
|
-
{RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
|
|
1633
|
-
{RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
|
|
1634
|
-
}},
|
|
1635
|
-
{RPCResult::Type::STR, "signalling", "indicates blocks that signalled with a # and blocks that did not with a -"},
|
|
1636
|
-
}},
|
|
1637
|
-
};
|
|
1638
|
-
|
|
1639
|
-
UniValue DeploymentInfo(const CBlockIndex* blockindex, const Consensus::Params& consensusParams)
|
|
1640
|
-
{
|
|
1641
|
-
UniValue softforks(UniValue::VOBJ);
|
|
1642
|
-
SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB);
|
|
1643
|
-
SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG);
|
|
1644
|
-
SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_CLTV);
|
|
1645
|
-
SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_CSV);
|
|
1646
|
-
SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_SEGWIT);
|
|
1647
|
-
SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
|
|
1648
|
-
SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_TAPROOT);
|
|
1649
|
-
return softforks;
|
|
1650
|
-
}
|
|
1651
|
-
} // anon namespace
|
|
1652
|
-
|
|
1653
|
-
static RPCHelpMan getdeploymentinfo()
|
|
1654
|
-
{
|
|
1655
|
-
return RPCHelpMan{"getdeploymentinfo",
|
|
1656
|
-
"Returns an object containing various state info regarding deployments of consensus changes.",
|
|
1657
|
-
{
|
|
1658
|
-
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Default{"hash of current chain tip"}, "The block hash at which to query deployment state"},
|
|
1659
|
-
},
|
|
1660
|
-
RPCResult{
|
|
1661
|
-
RPCResult::Type::OBJ, "", "", {
|
|
1662
|
-
{RPCResult::Type::STR, "hash", "requested block hash (or tip)"},
|
|
1663
|
-
{RPCResult::Type::NUM, "height", "requested block height (or tip)"},
|
|
1664
|
-
{RPCResult::Type::OBJ, "deployments", "", {
|
|
1665
|
-
{RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment}
|
|
1666
|
-
}},
|
|
1667
|
-
}
|
|
1668
|
-
},
|
|
1669
|
-
RPCExamples{ HelpExampleCli("getdeploymentinfo", "") + HelpExampleRpc("getdeploymentinfo", "") },
|
|
1670
|
-
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
1671
|
-
{
|
|
1672
|
-
const ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
|
1673
|
-
LOCK(cs_main);
|
|
1674
|
-
const CChainState& active_chainstate = chainman.ActiveChainstate();
|
|
1675
|
-
|
|
1676
|
-
const CBlockIndex* blockindex;
|
|
1677
|
-
if (request.params[0].isNull()) {
|
|
1678
|
-
blockindex = active_chainstate.m_chain.Tip();
|
|
1679
|
-
CHECK_NONFATAL(blockindex);
|
|
1680
|
-
} else {
|
|
1681
|
-
const uint256 hash(ParseHashV(request.params[0], "blockhash"));
|
|
1682
|
-
blockindex = chainman.m_blockman.LookupBlockIndex(hash);
|
|
1683
|
-
if (!blockindex) {
|
|
1684
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
const Consensus::Params& consensusParams = Params().GetConsensus();
|
|
1689
|
-
|
|
1690
|
-
UniValue deploymentinfo(UniValue::VOBJ);
|
|
1691
|
-
deploymentinfo.pushKV("hash", blockindex->GetBlockHash().ToString());
|
|
1692
|
-
deploymentinfo.pushKV("height", blockindex->nHeight);
|
|
1693
|
-
deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, consensusParams));
|
|
1694
|
-
return deploymentinfo;
|
|
1695
|
-
},
|
|
1696
|
-
};
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
1472
|
/** Comparison function for sorting the getchaintips heads. */
|
|
1700
1473
|
struct CompareBlocksByHeight
|
|
1701
1474
|
{
|
|
@@ -1821,8 +1594,6 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
|
|
|
1821
1594
|
ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
|
|
1822
1595
|
size_t maxmempool = gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
|
|
1823
1596
|
ret.pushKV("maxmempool", (int64_t) maxmempool);
|
|
1824
|
-
ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
|
|
1825
|
-
ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
|
|
1826
1597
|
ret.pushKV("unbroadcastcount", uint64_t{pool.GetUnbroadcastTxs().size()});
|
|
1827
1598
|
return ret;
|
|
1828
1599
|
}
|
|
@@ -1841,7 +1612,6 @@ static RPCHelpMan getmempoolinfo()
|
|
|
1841
1612
|
{RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
|
|
1842
1613
|
{RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"},
|
|
1843
1614
|
{RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
|
|
1844
|
-
{RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
|
|
1845
1615
|
{RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
|
|
1846
1616
|
{RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"}
|
|
1847
1617
|
}},
|
|
@@ -2197,7 +1967,7 @@ static RPCHelpMan getblockstats()
|
|
|
2197
1967
|
const bool do_medianfee = do_all || stats.count("medianfee") != 0;
|
|
2198
1968
|
const bool do_feerate_percentiles = do_all || stats.count("feerate_percentiles") != 0;
|
|
2199
1969
|
const bool loop_inputs = do_all || do_medianfee || do_feerate_percentiles ||
|
|
2200
|
-
SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", "avgfeerate", "minfee", "
|
|
1970
|
+
SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", "avgfeerate", "minfee", "minfeerate", "maxfeerate");
|
|
2201
1971
|
const bool loop_outputs = do_all || loop_inputs || stats.count("total_out");
|
|
2202
1972
|
const bool do_calculate_size = do_mediantxsize ||
|
|
2203
1973
|
SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "swtotal_size");
|
|
@@ -2312,7 +2082,6 @@ static RPCHelpMan getblockstats()
|
|
|
2312
2082
|
ret_all.pushKV("feerate_percentiles", feerates_res);
|
|
2313
2083
|
ret_all.pushKV("height", (int64_t)pindex->nHeight);
|
|
2314
2084
|
ret_all.pushKV("ins", inputs);
|
|
2315
|
-
ret_all.pushKV("maxfee", maxfee);
|
|
2316
2085
|
ret_all.pushKV("maxfeerate", maxfeerate);
|
|
2317
2086
|
ret_all.pushKV("maxtxsize", maxtxsize);
|
|
2318
2087
|
ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
|
|
@@ -2322,7 +2091,7 @@ static RPCHelpMan getblockstats()
|
|
|
2322
2091
|
ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate);
|
|
2323
2092
|
ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize);
|
|
2324
2093
|
ret_all.pushKV("outs", outputs);
|
|
2325
|
-
ret_all.pushKV("subsidy",
|
|
2094
|
+
ret_all.pushKV("subsidy", GetProofOfWorkReward(pindex->nBits, pindex->nTime));
|
|
2326
2095
|
ret_all.pushKV("swtotal_size", swtotal_size);
|
|
2327
2096
|
ret_all.pushKV("swtotal_weight", swtotal_weight);
|
|
2328
2097
|
ret_all.pushKV("swtxs", swtxs);
|
|
@@ -2842,7 +2611,6 @@ static const CRPCCommand commands[] =
|
|
|
2842
2611
|
{ "blockchain", &getblockheader, },
|
|
2843
2612
|
{ "blockchain", &getchaintips, },
|
|
2844
2613
|
{ "blockchain", &getdifficulty, },
|
|
2845
|
-
{ "blockchain", &getdeploymentinfo, },
|
|
2846
2614
|
{ "blockchain", &getmempoolancestors, },
|
|
2847
2615
|
{ "blockchain", &getmempooldescendants, },
|
|
2848
2616
|
{ "blockchain", &getmempoolentry, },
|
|
@@ -2850,10 +2618,8 @@ static const CRPCCommand commands[] =
|
|
|
2850
2618
|
{ "blockchain", &getrawmempool, },
|
|
2851
2619
|
{ "blockchain", &gettxout, },
|
|
2852
2620
|
{ "blockchain", &gettxoutsetinfo, },
|
|
2853
|
-
{ "blockchain", &pruneblockchain, },
|
|
2854
2621
|
{ "blockchain", &savemempool, },
|
|
2855
2622
|
{ "blockchain", &verifychain, },
|
|
2856
|
-
|
|
2857
2623
|
{ "blockchain", &preciousblock, },
|
|
2858
2624
|
{ "blockchain", &scantxoutset, },
|
|
2859
2625
|
{ "blockchain", &getblockfilter, },
|
|
@@ -34,7 +34,7 @@ static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
|
|
|
34
34
|
* @return A floating point number that is a multiple of the main net minimum
|
|
35
35
|
* difficulty (4295032833 hashes).
|
|
36
36
|
*/
|
|
37
|
-
double GetDifficulty(const CBlockIndex* blockindex);
|
|
37
|
+
double GetDifficulty(const CBlockIndex* blockindex, const CBlockIndex* tip);
|
|
38
38
|
|
|
39
39
|
/** Callback for when block tip changed. */
|
|
40
40
|
void RPCNotifyBlockChange(const CBlockIndex*);
|
|
@@ -70,6 +70,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|
|
70
70
|
{ "listtransactions", 2, "skip" },
|
|
71
71
|
{ "listtransactions", 3, "include_watchonly" },
|
|
72
72
|
{ "walletpassphrase", 1, "timeout" },
|
|
73
|
+
{ "walletpassphrase", 2, "mintonly" },
|
|
73
74
|
{ "getblocktemplate", 0, "template_request" },
|
|
74
75
|
{ "listsinceblock", 1, "target_confirmations" },
|
|
75
76
|
{ "listsinceblock", 2, "include_watchonly" },
|
|
@@ -102,7 +103,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|
|
102
103
|
{ "createrawtransaction", 0, "inputs" },
|
|
103
104
|
{ "createrawtransaction", 1, "outputs" },
|
|
104
105
|
{ "createrawtransaction", 2, "locktime" },
|
|
105
|
-
{ "createrawtransaction", 3, "
|
|
106
|
+
{ "createrawtransaction", 3, "timestamp" },
|
|
106
107
|
{ "decoderawtransaction", 1, "iswitness" },
|
|
107
108
|
{ "signrawtransactionwithkey", 1, "privkeys" },
|
|
108
109
|
{ "signrawtransactionwithkey", 2, "prevtxs" },
|
|
@@ -116,15 +117,16 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|
|
116
117
|
{ "walletcreatefundedpsbt", 0, "inputs" },
|
|
117
118
|
{ "walletcreatefundedpsbt", 1, "outputs" },
|
|
118
119
|
{ "walletcreatefundedpsbt", 2, "locktime" },
|
|
119
|
-
{ "walletcreatefundedpsbt", 3, "
|
|
120
|
-
{ "walletcreatefundedpsbt", 4, "
|
|
120
|
+
{ "walletcreatefundedpsbt", 3, "timestamp" },
|
|
121
|
+
{ "walletcreatefundedpsbt", 4, "options" },
|
|
122
|
+
{ "walletcreatefundedpsbt", 5, "bip32derivs" },
|
|
121
123
|
{ "walletprocesspsbt", 1, "sign" },
|
|
122
124
|
{ "walletprocesspsbt", 3, "bip32derivs" },
|
|
123
125
|
{ "walletprocesspsbt", 4, "finalize" },
|
|
124
126
|
{ "createpsbt", 0, "inputs" },
|
|
125
127
|
{ "createpsbt", 1, "outputs" },
|
|
126
128
|
{ "createpsbt", 2, "locktime" },
|
|
127
|
-
{ "createpsbt", 3, "
|
|
129
|
+
{ "createpsbt", 3, "timestamp" },
|
|
128
130
|
{ "combinepsbt", 0, "txs"},
|
|
129
131
|
{ "joinpsbts", 0, "txs"},
|
|
130
132
|
{ "finalizepsbt", 1, "extract"},
|
|
@@ -154,22 +156,16 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|
|
154
156
|
{ "verifychain", 1, "nblocks" },
|
|
155
157
|
{ "getblockstats", 0, "hash_or_height" },
|
|
156
158
|
{ "getblockstats", 1, "stats" },
|
|
157
|
-
{ "pruneblockchain", 0, "height" },
|
|
158
159
|
{ "keypoolrefill", 0, "newsize" },
|
|
159
160
|
{ "getrawmempool", 0, "verbose" },
|
|
160
161
|
{ "getrawmempool", 1, "mempool_sequence" },
|
|
161
162
|
{ "estimatesmartfee", 0, "conf_target" },
|
|
162
|
-
{ "estimaterawfee", 0, "conf_target" },
|
|
163
|
-
{ "estimaterawfee", 1, "threshold" },
|
|
164
|
-
{ "prioritisetransaction", 1, "dummy" },
|
|
165
|
-
{ "prioritisetransaction", 2, "fee_delta" },
|
|
166
163
|
{ "setban", 2, "bantime" },
|
|
167
164
|
{ "setban", 3, "absolute" },
|
|
168
165
|
{ "setnetworkactive", 0, "state" },
|
|
169
166
|
{ "setwalletflag", 1, "value" },
|
|
170
167
|
{ "getmempoolancestors", 1, "verbose" },
|
|
171
168
|
{ "getmempooldescendants", 1, "verbose" },
|
|
172
|
-
{ "bumpfee", 1, "options" },
|
|
173
169
|
{ "psbtbumpfee", 1, "options" },
|
|
174
170
|
{ "logging", 0, "include" },
|
|
175
171
|
{ "logging", 1, "exclude" },
|
|
@@ -201,6 +197,16 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|
|
201
197
|
{ "addpeeraddress", 1, "port"},
|
|
202
198
|
{ "addpeeraddress", 2, "tried"},
|
|
203
199
|
{ "stop", 0, "wait" },
|
|
200
|
+
// peercoin:
|
|
201
|
+
{ "importcoinstake", 1, "timestamp" },
|
|
202
|
+
{ "listminting", 0, "count" },
|
|
203
|
+
{ "reservebalance", 0, "reserve" },
|
|
204
|
+
{ "reservebalance", 1, "amount" },
|
|
205
|
+
{ "sendalert", 2, "minver"},
|
|
206
|
+
{ "sendalert", 3, "maxver"},
|
|
207
|
+
{ "sendalert", 4, "priority"},
|
|
208
|
+
{ "sendalert", 5, "id"},
|
|
209
|
+
{ "sendalert", 6, "cancelupto"},
|
|
204
210
|
};
|
|
205
211
|
// clang-format on
|
|
206
212
|
|
|
@@ -11,12 +11,11 @@
|
|
|
11
11
|
#include <consensus/validation.h>
|
|
12
12
|
#include <core_io.h>
|
|
13
13
|
#include <deploymentinfo.h>
|
|
14
|
-
#include <deploymentstatus.h>
|
|
15
14
|
#include <key_io.h>
|
|
15
|
+
#include <interfaces/wallet.h>
|
|
16
16
|
#include <net.h>
|
|
17
17
|
#include <node/context.h>
|
|
18
18
|
#include <node/miner.h>
|
|
19
|
-
#include <policy/fees.h>
|
|
20
19
|
#include <pow.h>
|
|
21
20
|
#include <rpc/blockchain.h>
|
|
22
21
|
#include <rpc/mining.h>
|
|
@@ -29,7 +28,6 @@
|
|
|
29
28
|
#include <shutdown.h>
|
|
30
29
|
#include <txmempool.h>
|
|
31
30
|
#include <univalue.h>
|
|
32
|
-
#include <util/fees.h>
|
|
33
31
|
#include <util/strencodings.h>
|
|
34
32
|
#include <util/string.h>
|
|
35
33
|
#include <util/system.h>
|
|
@@ -38,6 +36,12 @@
|
|
|
38
36
|
#include <validationinterface.h>
|
|
39
37
|
#include <warnings.h>
|
|
40
38
|
|
|
39
|
+
#include <wallet/rpc/util.h>
|
|
40
|
+
#include <wallet/rpc/wallet.h>
|
|
41
|
+
#include <wallet/wallet.h>
|
|
42
|
+
|
|
43
|
+
#include <kernel.h>
|
|
44
|
+
|
|
41
45
|
#include <memory>
|
|
42
46
|
#include <stdint.h>
|
|
43
47
|
|
|
@@ -47,6 +51,9 @@ using node::IncrementExtraNonce;
|
|
|
47
51
|
using node::NodeContext;
|
|
48
52
|
using node::RegenerateCommitments;
|
|
49
53
|
using node::UpdateTime;
|
|
54
|
+
using interfaces::WalletLoader;
|
|
55
|
+
|
|
56
|
+
using wallet::GetWalletForJSONRPCRequest;
|
|
50
57
|
|
|
51
58
|
/**
|
|
52
59
|
* Return average network hashes per second based on the last 'lookup' blocks,
|
|
@@ -63,9 +70,10 @@ static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_ch
|
|
|
63
70
|
if (pb == nullptr || !pb->nHeight)
|
|
64
71
|
return 0;
|
|
65
72
|
|
|
73
|
+
//ppcTODO - redo this to fit peercoin
|
|
66
74
|
// If lookup is -1, then use blocks since last difficulty change.
|
|
67
|
-
if (lookup <= 0)
|
|
68
|
-
lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1;
|
|
75
|
+
// if (lookup <= 0)
|
|
76
|
+
// lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1;
|
|
69
77
|
|
|
70
78
|
// If lookup is larger than chain, then set it to chain length.
|
|
71
79
|
if (lookup > pb->nHeight)
|
|
@@ -85,7 +93,7 @@ static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_ch
|
|
|
85
93
|
if (minTime == maxTime)
|
|
86
94
|
return 0;
|
|
87
95
|
|
|
88
|
-
arith_uint256 workDiff = pb->
|
|
96
|
+
arith_uint256 workDiff = pb->nChainTrust - pb0->nChainTrust;
|
|
89
97
|
int64_t timeDiff = maxTime - minTime;
|
|
90
98
|
|
|
91
99
|
return workDiff.getdouble() / timeDiff;
|
|
@@ -116,6 +124,47 @@ static RPCHelpMan getnetworkhashps()
|
|
|
116
124
|
};
|
|
117
125
|
}
|
|
118
126
|
|
|
127
|
+
// peercoin: get network Gh/s estimate
|
|
128
|
+
static RPCHelpMan getnetworkghps()
|
|
129
|
+
{
|
|
130
|
+
return RPCHelpMan{"getnetworkghps",
|
|
131
|
+
"\nReturns a recent Ghash/second network mining estimate.\n",
|
|
132
|
+
{
|
|
133
|
+
},
|
|
134
|
+
RPCResult{
|
|
135
|
+
RPCResult::Type::NUM, "", "Ghashes per second estimated"},
|
|
136
|
+
RPCExamples{
|
|
137
|
+
HelpExampleCli("getnetworkghps", "")
|
|
138
|
+
+ HelpExampleRpc("getnetworkghps", "")
|
|
139
|
+
},
|
|
140
|
+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
141
|
+
{
|
|
142
|
+
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
|
143
|
+
LOCK(cs_main);
|
|
144
|
+
|
|
145
|
+
int64_t nTargetSpacingWorkMin = 30;
|
|
146
|
+
int64_t nTargetSpacingWork = nTargetSpacingWorkMin;
|
|
147
|
+
int64_t nInterval = 72;
|
|
148
|
+
CBlockIndex* pindex = chainman.ActiveChain().Genesis();
|
|
149
|
+
CBlockIndex* pindexPrevWork = chainman.ActiveChain().Genesis();
|
|
150
|
+
while (pindex)
|
|
151
|
+
{
|
|
152
|
+
// Exponential moving average of recent proof-of-work block spacing
|
|
153
|
+
if (pindex->IsProofOfWork())
|
|
154
|
+
{
|
|
155
|
+
int64_t nActualSpacingWork = pindex->GetBlockTime() - pindexPrevWork->GetBlockTime();
|
|
156
|
+
nTargetSpacingWork = ((nInterval - 1) * nTargetSpacingWork + nActualSpacingWork + nActualSpacingWork) / (nInterval + 1);
|
|
157
|
+
nTargetSpacingWork = std::max(nTargetSpacingWork, nTargetSpacingWorkMin);
|
|
158
|
+
pindexPrevWork = pindex;
|
|
159
|
+
}
|
|
160
|
+
pindex = chainman.ActiveChain().Next(pindex);
|
|
161
|
+
}
|
|
162
|
+
double dNetworkGhps = GetDifficulty(pindex, chainman.ActiveChain().Tip()) * 4.294967296 / nTargetSpacingWork;
|
|
163
|
+
return dNetworkGhps;
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
119
168
|
static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t& max_tries, unsigned int& extra_nonce, uint256& block_hash)
|
|
120
169
|
{
|
|
121
170
|
block_hash.SetNull();
|
|
@@ -147,7 +196,7 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
|
|
|
147
196
|
return true;
|
|
148
197
|
}
|
|
149
198
|
|
|
150
|
-
static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
|
|
199
|
+
static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& mempool, const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries, NodeContext* m_node)
|
|
151
200
|
{
|
|
152
201
|
int nHeightEnd = 0;
|
|
153
202
|
int nHeight = 0;
|
|
@@ -161,7 +210,7 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
|
|
|
161
210
|
UniValue blockHashes(UniValue::VARR);
|
|
162
211
|
while (nHeight < nHeightEnd && !ShutdownRequested())
|
|
163
212
|
{
|
|
164
|
-
std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(chainman.ActiveChainstate(), mempool, Params()).CreateNewBlock(coinbase_script));
|
|
213
|
+
std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(chainman.ActiveChainstate(), mempool, Params()).CreateNewBlock(coinbase_script,nullptr,nullptr,m_node));
|
|
165
214
|
if (!pblocktemplate.get())
|
|
166
215
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
|
|
167
216
|
CBlock *pblock = &pblocktemplate->block;
|
|
@@ -246,9 +295,9 @@ static RPCHelpMan generatetodescriptor()
|
|
|
246
295
|
const CTxMemPool& mempool = EnsureMemPool(node);
|
|
247
296
|
ChainstateManager& chainman = EnsureChainman(node);
|
|
248
297
|
|
|
249
|
-
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
|
|
298
|
+
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries, &node);
|
|
250
299
|
},
|
|
251
|
-
|
|
300
|
+
};
|
|
252
301
|
}
|
|
253
302
|
|
|
254
303
|
static RPCHelpMan generate()
|
|
@@ -264,7 +313,7 @@ static RPCHelpMan generatetoaddress()
|
|
|
264
313
|
"Mine to a specified address and return the block hashes.",
|
|
265
314
|
{
|
|
266
315
|
{"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated."},
|
|
267
|
-
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated
|
|
316
|
+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated peercoin to."},
|
|
268
317
|
{"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."},
|
|
269
318
|
},
|
|
270
319
|
RPCResult{
|
|
@@ -275,14 +324,13 @@ static RPCHelpMan generatetoaddress()
|
|
|
275
324
|
RPCExamples{
|
|
276
325
|
"\nGenerate 11 blocks to myaddress\n"
|
|
277
326
|
+ HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
|
|
278
|
-
+ "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated
|
|
327
|
+
+ "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated peercoin to with:\n"
|
|
279
328
|
+ HelpExampleCli("getnewaddress", "")
|
|
280
329
|
},
|
|
281
330
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
282
331
|
{
|
|
283
332
|
const int num_blocks{request.params[0].get_int()};
|
|
284
333
|
const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
|
|
285
|
-
|
|
286
334
|
CTxDestination destination = DecodeDestination(request.params[1].get_str());
|
|
287
335
|
if (!IsValidDestination(destination)) {
|
|
288
336
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
|
|
@@ -294,7 +342,7 @@ static RPCHelpMan generatetoaddress()
|
|
|
294
342
|
|
|
295
343
|
CScript coinbase_script = GetScriptForDestination(destination);
|
|
296
344
|
|
|
297
|
-
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
|
|
345
|
+
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries, &node);
|
|
298
346
|
},
|
|
299
347
|
};
|
|
300
348
|
}
|
|
@@ -443,8 +491,9 @@ static RPCHelpMan getmininginfo()
|
|
|
443
491
|
obj.pushKV("blocks", active_chain.Height());
|
|
444
492
|
if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
|
|
445
493
|
if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
|
|
446
|
-
obj.pushKV("difficulty", (double)GetDifficulty(active_chain.Tip()));
|
|
494
|
+
obj.pushKV("difficulty", (double)GetDifficulty(active_chain.Tip(), active_chain.Tip()));
|
|
447
495
|
obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
|
|
496
|
+
obj.pushKV("networkghps", getnetworkghps().HandleRequest(request));
|
|
448
497
|
obj.pushKV("pooledtx", (uint64_t)mempool.size());
|
|
449
498
|
obj.pushKV("chain", Params().NetworkIDString());
|
|
450
499
|
obj.pushKV("warnings", GetWarnings(false).original);
|
|
@@ -454,43 +503,6 @@ static RPCHelpMan getmininginfo()
|
|
|
454
503
|
}
|
|
455
504
|
|
|
456
505
|
|
|
457
|
-
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
|
|
458
|
-
static RPCHelpMan prioritisetransaction()
|
|
459
|
-
{
|
|
460
|
-
return RPCHelpMan{"prioritisetransaction",
|
|
461
|
-
"Accepts the transaction into mined blocks at a higher (or lower) priority\n",
|
|
462
|
-
{
|
|
463
|
-
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id."},
|
|
464
|
-
{"dummy", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "API-Compatibility for previous API. Must be zero or null.\n"
|
|
465
|
-
" DEPRECATED. For forward compatibility use named arguments and omit this parameter."},
|
|
466
|
-
{"fee_delta", RPCArg::Type::NUM, RPCArg::Optional::NO, "The fee value (in satoshis) to add (or subtract, if negative).\n"
|
|
467
|
-
" Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n"
|
|
468
|
-
" The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
|
|
469
|
-
" considers the transaction as it would have paid a higher (or lower) fee."},
|
|
470
|
-
},
|
|
471
|
-
RPCResult{
|
|
472
|
-
RPCResult::Type::BOOL, "", "Returns true"},
|
|
473
|
-
RPCExamples{
|
|
474
|
-
HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
|
|
475
|
-
+ HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
|
|
476
|
-
},
|
|
477
|
-
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
478
|
-
{
|
|
479
|
-
LOCK(cs_main);
|
|
480
|
-
|
|
481
|
-
uint256 hash(ParseHashV(request.params[0], "txid"));
|
|
482
|
-
CAmount nAmount = request.params[2].get_int64();
|
|
483
|
-
|
|
484
|
-
if (!(request.params[1].isNull() || request.params[1].get_real() == 0)) {
|
|
485
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.");
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
EnsureAnyMemPool(request.context).PrioritiseTransaction(hash, nAmount);
|
|
489
|
-
return true;
|
|
490
|
-
},
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
|
|
494
506
|
|
|
495
507
|
// NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
|
|
496
508
|
static UniValue BIP22ValidationResult(const BlockValidationState& state)
|
|
@@ -511,15 +523,6 @@ static UniValue BIP22ValidationResult(const BlockValidationState& state)
|
|
|
511
523
|
return "valid?";
|
|
512
524
|
}
|
|
513
525
|
|
|
514
|
-
static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
|
|
515
|
-
const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
|
|
516
|
-
std::string s = vbinfo.name;
|
|
517
|
-
if (!vbinfo.gbt_force) {
|
|
518
|
-
s.insert(s.begin(), '!');
|
|
519
|
-
}
|
|
520
|
-
return s;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
526
|
static RPCHelpMan getblocktemplate()
|
|
524
527
|
{
|
|
525
528
|
return RPCHelpMan{"getblocktemplate",
|
|
@@ -772,7 +775,7 @@ static RPCHelpMan getblocktemplate()
|
|
|
772
775
|
|
|
773
776
|
// Create new block
|
|
774
777
|
CScript scriptDummy = CScript() << OP_TRUE;
|
|
775
|
-
pblocktemplate = BlockAssembler(active_chainstate, mempool, Params()).CreateNewBlock(scriptDummy);
|
|
778
|
+
pblocktemplate = BlockAssembler(active_chainstate, mempool, Params()).CreateNewBlock(scriptDummy, nullptr, nullptr, &node);
|
|
776
779
|
if (!pblocktemplate)
|
|
777
780
|
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
|
|
778
781
|
|
|
@@ -783,11 +786,11 @@ static RPCHelpMan getblocktemplate()
|
|
|
783
786
|
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
|
|
784
787
|
|
|
785
788
|
// Update nTime
|
|
786
|
-
UpdateTime(pblock
|
|
789
|
+
UpdateTime(pblock);
|
|
787
790
|
pblock->nNonce = 0;
|
|
788
791
|
|
|
789
792
|
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
|
|
790
|
-
const bool fPreSegWit = !
|
|
793
|
+
const bool fPreSegWit = !IsBTC16BIPsEnabled(active_chain.Tip()->nTime);
|
|
791
794
|
|
|
792
795
|
UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
|
|
793
796
|
|
|
@@ -850,57 +853,12 @@ static RPCHelpMan getblocktemplate()
|
|
|
850
853
|
aRules.push_back("!signet");
|
|
851
854
|
}
|
|
852
855
|
|
|
853
|
-
UniValue vbavailable(UniValue::VOBJ);
|
|
854
|
-
for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
|
|
855
|
-
Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
|
|
856
|
-
ThresholdState state = g_versionbitscache.State(pindexPrev, consensusParams, pos);
|
|
857
|
-
switch (state) {
|
|
858
|
-
case ThresholdState::DEFINED:
|
|
859
|
-
case ThresholdState::FAILED:
|
|
860
|
-
// Not exposed to GBT at all
|
|
861
|
-
break;
|
|
862
|
-
case ThresholdState::LOCKED_IN:
|
|
863
|
-
// Ensure bit is set in block version
|
|
864
|
-
pblock->nVersion |= g_versionbitscache.Mask(consensusParams, pos);
|
|
865
|
-
[[fallthrough]];
|
|
866
|
-
case ThresholdState::STARTED:
|
|
867
|
-
{
|
|
868
|
-
const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
|
|
869
|
-
vbavailable.pushKV(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit);
|
|
870
|
-
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
|
|
871
|
-
if (!vbinfo.gbt_force) {
|
|
872
|
-
// If the client doesn't support this, don't indicate it in the [default] version
|
|
873
|
-
pblock->nVersion &= ~g_versionbitscache.Mask(consensusParams, pos);
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
break;
|
|
877
|
-
}
|
|
878
|
-
case ThresholdState::ACTIVE:
|
|
879
|
-
{
|
|
880
|
-
// Add to rules only
|
|
881
|
-
const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
|
|
882
|
-
aRules.push_back(gbt_vb_name(pos));
|
|
883
|
-
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
|
|
884
|
-
// Not supported by the client; make sure it's safe to proceed
|
|
885
|
-
if (!vbinfo.gbt_force) {
|
|
886
|
-
// If we do anything other than throw an exception here, be sure version/force isn't sent to old clients
|
|
887
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Support for '%s' rule requires explicit client support", vbinfo.name));
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
break;
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
856
|
result.pushKV("version", pblock->nVersion);
|
|
895
857
|
result.pushKV("rules", aRules);
|
|
896
|
-
result.pushKV("vbavailable", vbavailable);
|
|
897
|
-
result.pushKV("vbrequired", int(0));
|
|
898
858
|
|
|
899
859
|
if (nMaxVersionPreVB >= 2) {
|
|
900
|
-
// If VB is supported by the client, nMaxVersionPreVB is -1, so we won't get here
|
|
901
860
|
// Because BIP 34 changed how the generation transaction is serialized, we can only use version/force back to v2 blocks
|
|
902
861
|
// This is safe to do [otherwise-]unconditionally only because we are throwing an exception above if a non-force deployment gets activated
|
|
903
|
-
// Note that this can probably also be removed entirely after the first BIP9 non-force deployment (ie, probably segwit) gets activated
|
|
904
862
|
aMutable.push_back("version/force");
|
|
905
863
|
}
|
|
906
864
|
|
|
@@ -1006,6 +964,13 @@ static RPCHelpMan submitblock()
|
|
|
1006
964
|
}
|
|
1007
965
|
}
|
|
1008
966
|
|
|
967
|
+
// peercoin: check block before attempting to sign it
|
|
968
|
+
BlockValidationState state;
|
|
969
|
+
if (!CheckBlock(block, state, Params().GetConsensus(), true, true, false)) {
|
|
970
|
+
LogPrintf("SubmitBlock: %s\n", state.ToString());
|
|
971
|
+
throw JSONRPCError(-100, "Block failed CheckBlock() function.");
|
|
972
|
+
}
|
|
973
|
+
|
|
1009
974
|
{
|
|
1010
975
|
LOCK(cs_main);
|
|
1011
976
|
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
|
|
@@ -1059,7 +1024,8 @@ static RPCHelpMan submitheader()
|
|
|
1059
1024
|
}
|
|
1060
1025
|
|
|
1061
1026
|
BlockValidationState state;
|
|
1062
|
-
|
|
1027
|
+
int tmpTemp;
|
|
1028
|
+
chainman.ProcessNewBlockHeaders(tmpTemp, chainman.ActiveChain().Tip()->GetBlockHash(), {h}, state, Params());
|
|
1063
1029
|
if (state.IsValid()) return NullUniValue;
|
|
1064
1030
|
if (state.IsError()) {
|
|
1065
1031
|
throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
|
|
@@ -1084,7 +1050,7 @@ static RPCHelpMan estimatesmartfee()
|
|
|
1084
1050
|
" higher feerate and is more likely to be sufficient for the desired\n"
|
|
1085
1051
|
" target, but is not as responsive to short term drops in the\n"
|
|
1086
1052
|
" prevailing fee market. Must be one of (case insensitive):\n"
|
|
1087
|
-
"\""
|
|
1053
|
+
"\""},
|
|
1088
1054
|
},
|
|
1089
1055
|
RPCResult{
|
|
1090
1056
|
RPCResult::Type::OBJ, "", "",
|
|
@@ -1109,159 +1075,14 @@ static RPCHelpMan estimatesmartfee()
|
|
|
1109
1075
|
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
|
|
1110
1076
|
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
|
|
1111
1077
|
|
|
1112
|
-
|
|
1113
|
-
const NodeContext& node = EnsureAnyNodeContext(request.context);
|
|
1114
|
-
const CTxMemPool& mempool = EnsureMemPool(node);
|
|
1115
|
-
|
|
1116
|
-
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
|
|
1117
|
-
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
|
|
1118
|
-
bool conservative = true;
|
|
1119
|
-
if (!request.params[1].isNull()) {
|
|
1120
|
-
FeeEstimateMode fee_mode;
|
|
1121
|
-
if (!FeeModeFromString(request.params[1].get_str(), fee_mode)) {
|
|
1122
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());
|
|
1123
|
-
}
|
|
1124
|
-
if (fee_mode == FeeEstimateMode::ECONOMICAL) conservative = false;
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
UniValue result(UniValue::VOBJ);
|
|
1128
|
-
UniValue errors(UniValue::VARR);
|
|
1129
|
-
FeeCalculation feeCalc;
|
|
1130
|
-
CFeeRate feeRate{fee_estimator.estimateSmartFee(conf_target, &feeCalc, conservative)};
|
|
1131
|
-
if (feeRate != CFeeRate(0)) {
|
|
1132
|
-
CFeeRate min_mempool_feerate{mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000)};
|
|
1133
|
-
CFeeRate min_relay_feerate{::minRelayTxFee};
|
|
1134
|
-
feeRate = std::max({feeRate, min_mempool_feerate, min_relay_feerate});
|
|
1135
|
-
result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
|
|
1136
|
-
} else {
|
|
1137
|
-
errors.push_back("Insufficient data or no feerate found");
|
|
1138
|
-
result.pushKV("errors", errors);
|
|
1139
|
-
}
|
|
1140
|
-
result.pushKV("blocks", feeCalc.returnedTarget);
|
|
1141
|
-
return result;
|
|
1142
|
-
},
|
|
1143
|
-
};
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
static RPCHelpMan estimaterawfee()
|
|
1147
|
-
{
|
|
1148
|
-
return RPCHelpMan{"estimaterawfee",
|
|
1149
|
-
"\nWARNING: This interface is unstable and may disappear or change!\n"
|
|
1150
|
-
"\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
|
|
1151
|
-
" implementation of fee estimation. The parameters it can be called with\n"
|
|
1152
|
-
" and the results it returns will change if the internal implementation changes.\n"
|
|
1153
|
-
"\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
|
|
1154
|
-
"confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"
|
|
1155
|
-
"defined in BIP 141 (witness data is discounted).\n",
|
|
1156
|
-
{
|
|
1157
|
-
{"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
|
|
1158
|
-
{"threshold", RPCArg::Type::NUM, RPCArg::Default{0.95}, "The proportion of transactions in a given feerate range that must have been\n"
|
|
1159
|
-
" confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"
|
|
1160
|
-
" lower buckets."},
|
|
1161
|
-
},
|
|
1162
|
-
RPCResult{
|
|
1163
|
-
RPCResult::Type::OBJ, "", "Results are returned for any horizon which tracks blocks up to the confirmation target",
|
|
1164
|
-
{
|
|
1165
|
-
{RPCResult::Type::OBJ, "short", /*optional=*/true, "estimate for short time horizon",
|
|
1166
|
-
{
|
|
1167
|
-
{RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB"},
|
|
1168
|
-
{RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"},
|
|
1169
|
-
{RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"},
|
|
1170
|
-
{RPCResult::Type::OBJ, "pass", /*optional=*/true, "information about the lowest range of feerates to succeed in meeting the threshold",
|
|
1171
|
-
{
|
|
1172
|
-
{RPCResult::Type::NUM, "startrange", "start of feerate range"},
|
|
1173
|
-
{RPCResult::Type::NUM, "endrange", "end of feerate range"},
|
|
1174
|
-
{RPCResult::Type::NUM, "withintarget", "number of txs over history horizon in the feerate range that were confirmed within target"},
|
|
1175
|
-
{RPCResult::Type::NUM, "totalconfirmed", "number of txs over history horizon in the feerate range that were confirmed at any point"},
|
|
1176
|
-
{RPCResult::Type::NUM, "inmempool", "current number of txs in mempool in the feerate range unconfirmed for at least target blocks"},
|
|
1177
|
-
{RPCResult::Type::NUM, "leftmempool", "number of txs over history horizon in the feerate range that left mempool unconfirmed after target"},
|
|
1178
|
-
}},
|
|
1179
|
-
{RPCResult::Type::OBJ, "fail", /*optional=*/true, "information about the highest range of feerates to fail to meet the threshold",
|
|
1180
|
-
{
|
|
1181
|
-
{RPCResult::Type::ELISION, "", ""},
|
|
1182
|
-
}},
|
|
1183
|
-
{RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",
|
|
1184
|
-
{
|
|
1185
|
-
{RPCResult::Type::STR, "error", ""},
|
|
1186
|
-
}},
|
|
1187
|
-
}},
|
|
1188
|
-
{RPCResult::Type::OBJ, "medium", /*optional=*/true, "estimate for medium time horizon",
|
|
1189
|
-
{
|
|
1190
|
-
{RPCResult::Type::ELISION, "", ""},
|
|
1191
|
-
}},
|
|
1192
|
-
{RPCResult::Type::OBJ, "long", /*optional=*/true, "estimate for long time horizon",
|
|
1193
|
-
{
|
|
1194
|
-
{RPCResult::Type::ELISION, "", ""},
|
|
1195
|
-
}},
|
|
1196
|
-
}},
|
|
1197
|
-
RPCExamples{
|
|
1198
|
-
HelpExampleCli("estimaterawfee", "6 0.9")
|
|
1199
|
-
},
|
|
1200
|
-
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
1201
|
-
{
|
|
1202
|
-
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
|
|
1203
|
-
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
|
|
1204
|
-
|
|
1205
|
-
CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
|
|
1206
|
-
|
|
1207
|
-
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
|
|
1208
|
-
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
|
|
1209
|
-
double threshold = 0.95;
|
|
1210
|
-
if (!request.params[1].isNull()) {
|
|
1211
|
-
threshold = request.params[1].get_real();
|
|
1212
|
-
}
|
|
1213
|
-
if (threshold < 0 || threshold > 1) {
|
|
1214
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid threshold");
|
|
1215
|
-
}
|
|
1078
|
+
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
|
1216
1079
|
|
|
1217
1080
|
UniValue result(UniValue::VOBJ);
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
CFeeRate feeRate;
|
|
1221
|
-
EstimationResult buckets;
|
|
1222
|
-
|
|
1223
|
-
// Only output results for horizons which track the target
|
|
1224
|
-
if (conf_target > fee_estimator.HighestTargetTracked(horizon)) continue;
|
|
1225
|
-
|
|
1226
|
-
feeRate = fee_estimator.estimateRawFee(conf_target, threshold, horizon, &buckets);
|
|
1227
|
-
UniValue horizon_result(UniValue::VOBJ);
|
|
1228
|
-
UniValue errors(UniValue::VARR);
|
|
1229
|
-
UniValue passbucket(UniValue::VOBJ);
|
|
1230
|
-
passbucket.pushKV("startrange", round(buckets.pass.start));
|
|
1231
|
-
passbucket.pushKV("endrange", round(buckets.pass.end));
|
|
1232
|
-
passbucket.pushKV("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0);
|
|
1233
|
-
passbucket.pushKV("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0);
|
|
1234
|
-
passbucket.pushKV("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0);
|
|
1235
|
-
passbucket.pushKV("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0);
|
|
1236
|
-
UniValue failbucket(UniValue::VOBJ);
|
|
1237
|
-
failbucket.pushKV("startrange", round(buckets.fail.start));
|
|
1238
|
-
failbucket.pushKV("endrange", round(buckets.fail.end));
|
|
1239
|
-
failbucket.pushKV("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0);
|
|
1240
|
-
failbucket.pushKV("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0);
|
|
1241
|
-
failbucket.pushKV("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0);
|
|
1242
|
-
failbucket.pushKV("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0);
|
|
1243
|
-
|
|
1244
|
-
// CFeeRate(0) is used to indicate error as a return value from estimateRawFee
|
|
1245
|
-
if (feeRate != CFeeRate(0)) {
|
|
1246
|
-
horizon_result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
|
|
1247
|
-
horizon_result.pushKV("decay", buckets.decay);
|
|
1248
|
-
horizon_result.pushKV("scale", (int)buckets.scale);
|
|
1249
|
-
horizon_result.pushKV("pass", passbucket);
|
|
1250
|
-
// buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output
|
|
1251
|
-
if (buckets.fail.start != -1) horizon_result.pushKV("fail", failbucket);
|
|
1252
|
-
} else {
|
|
1253
|
-
// Output only information that is still meaningful in the event of error
|
|
1254
|
-
horizon_result.pushKV("decay", buckets.decay);
|
|
1255
|
-
horizon_result.pushKV("scale", (int)buckets.scale);
|
|
1256
|
-
horizon_result.pushKV("fail", failbucket);
|
|
1257
|
-
errors.push_back("Insufficient data or no feerate found which meets threshold");
|
|
1258
|
-
horizon_result.pushKV("errors",errors);
|
|
1259
|
-
}
|
|
1260
|
-
result.pushKV(StringForFeeEstimateHorizon(horizon), horizon_result);
|
|
1261
|
-
}
|
|
1081
|
+
result.pushKV("feerate", 0.01);
|
|
1082
|
+
result.pushKV("blocks", chainman.ActiveChain().Height());
|
|
1262
1083
|
return result;
|
|
1263
1084
|
},
|
|
1264
|
-
|
|
1085
|
+
};
|
|
1265
1086
|
}
|
|
1266
1087
|
|
|
1267
1088
|
void RegisterMiningRPCCommands(CRPCTable &t)
|
|
@@ -1272,7 +1093,6 @@ static const CRPCCommand commands[] =
|
|
|
1272
1093
|
// --------------------- -----------------------
|
|
1273
1094
|
{ "mining", &getnetworkhashps, },
|
|
1274
1095
|
{ "mining", &getmininginfo, },
|
|
1275
|
-
{ "mining", &prioritisetransaction, },
|
|
1276
1096
|
{ "mining", &getblocktemplate, },
|
|
1277
1097
|
{ "mining", &submitblock, },
|
|
1278
1098
|
{ "mining", &submitheader, },
|
|
@@ -1283,8 +1103,6 @@ static const CRPCCommand commands[] =
|
|
|
1283
1103
|
{ "hidden", &generateblock, },
|
|
1284
1104
|
|
|
1285
1105
|
{ "util", &estimatesmartfee, },
|
|
1286
|
-
|
|
1287
|
-
{ "hidden", &estimaterawfee, },
|
|
1288
1106
|
{ "hidden", &generate, },
|
|
1289
1107
|
};
|
|
1290
1108
|
// clang-format on
|
|
@@ -41,15 +41,15 @@ static RPCHelpMan validateaddress()
|
|
|
41
41
|
{
|
|
42
42
|
return RPCHelpMan{
|
|
43
43
|
"validateaddress",
|
|
44
|
-
"\nReturn information about the given
|
|
44
|
+
"\nReturn information about the given peercoin address.\n",
|
|
45
45
|
{
|
|
46
|
-
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The
|
|
46
|
+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The peercoin address to validate"},
|
|
47
47
|
},
|
|
48
48
|
RPCResult{
|
|
49
49
|
RPCResult::Type::OBJ, "", "",
|
|
50
50
|
{
|
|
51
51
|
{RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"},
|
|
52
|
-
{RPCResult::Type::STR, "address", /*optional=*/true, "The
|
|
52
|
+
{RPCResult::Type::STR, "address", /*optional=*/true, "The peercoin address validated"},
|
|
53
53
|
{RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address"},
|
|
54
54
|
{RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script"},
|
|
55
55
|
{RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address"},
|
|
@@ -316,7 +316,7 @@ static RPCHelpMan verifymessage()
|
|
|
316
316
|
return RPCHelpMan{"verifymessage",
|
|
317
317
|
"Verify a signed message.",
|
|
318
318
|
{
|
|
319
|
-
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The
|
|
319
|
+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The peercoin address to use for the signature."},
|
|
320
320
|
{"signature", RPCArg::Type::STR, RPCArg::Optional::NO, "The signature provided by the signer in base 64 encoding (see signmessage)."},
|
|
321
321
|
{"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message that was signed."},
|
|
322
322
|
},
|
|
@@ -327,11 +327,11 @@ static RPCHelpMan verifymessage()
|
|
|
327
327
|
"\nUnlock the wallet for 30 seconds\n"
|
|
328
328
|
+ HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
|
|
329
329
|
"\nCreate the signature\n"
|
|
330
|
-
+ HelpExampleCli("signmessage", "\"
|
|
330
|
+
+ HelpExampleCli("signmessage", "\"PKRWHSDPDZHFJ9Mrjy65fPN3wL8YWQrD8q\" \"my message\"") +
|
|
331
331
|
"\nVerify the signature\n"
|
|
332
|
-
+ HelpExampleCli("verifymessage", "\"
|
|
332
|
+
+ HelpExampleCli("verifymessage", "\"PKRWHSDPDZHFJ9Mrjy65fPN3wL8YWQrD8q\" \"signature\" \"my message\"") +
|
|
333
333
|
"\nAs a JSON-RPC call\n"
|
|
334
|
-
+ HelpExampleRpc("verifymessage", "\"
|
|
334
|
+
+ HelpExampleRpc("verifymessage", "\"PKRWHSDPDZHFJ9Mrjy65fPN3wL8YWQrD8q\", \"signature\", \"my message\"")
|
|
335
335
|
},
|
|
336
336
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
337
337
|
{
|
|
@@ -375,7 +375,7 @@ static RPCHelpMan signmessagewithprivkey()
|
|
|
375
375
|
"\nCreate the signature\n"
|
|
376
376
|
+ HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") +
|
|
377
377
|
"\nVerify the signature\n"
|
|
378
|
-
+ HelpExampleCli("verifymessage", "\"
|
|
378
|
+
+ HelpExampleCli("verifymessage", "\"PKRWHSDPDZHFJ9Mrjy65fPN3wL8YWQrD8q\" \"signature\" \"my message\"") +
|
|
379
379
|
"\nAs a JSON-RPC call\n"
|
|
380
380
|
+ HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")
|
|
381
381
|
},
|
|
@@ -675,7 +675,7 @@ static RPCHelpMan echo(const std::string& name)
|
|
|
675
675
|
"\nSimply echo back the input arguments. This command is for testing.\n"
|
|
676
676
|
"\nIt will return an internal bug report when arg9='trigger_internal_bug' is passed.\n"
|
|
677
677
|
"\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in "
|
|
678
|
-
"
|
|
678
|
+
"peercoin-cli and the GUI. There is no server-side difference.",
|
|
679
679
|
{
|
|
680
680
|
{"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
|
681
681
|
{"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
#include <version.h>
|
|
29
29
|
#include <warnings.h>
|
|
30
30
|
|
|
31
|
+
#include <key_io.h>
|
|
31
32
|
#include <optional>
|
|
32
33
|
|
|
33
34
|
#include <univalue.h>
|
|
@@ -624,7 +625,7 @@ static RPCHelpMan getnetworkinfo()
|
|
|
624
625
|
{
|
|
625
626
|
LOCK(cs_main);
|
|
626
627
|
UniValue obj(UniValue::VOBJ);
|
|
627
|
-
obj.pushKV("version",
|
|
628
|
+
obj.pushKV("version", FormatFullVersion());
|
|
628
629
|
obj.pushKV("subversion", strSubVersion);
|
|
629
630
|
obj.pushKV("protocolversion",PROTOCOL_VERSION);
|
|
630
631
|
NodeContext& node = EnsureAnyNodeContext(request.context);
|
|
@@ -644,8 +645,6 @@ static RPCHelpMan getnetworkinfo()
|
|
|
644
645
|
obj.pushKV("connections_out", (int)node.connman->GetNodeCount(ConnectionDirection::Out));
|
|
645
646
|
}
|
|
646
647
|
obj.pushKV("networks", GetNetworksInfo());
|
|
647
|
-
obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
|
|
648
|
-
obj.pushKV("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK()));
|
|
649
648
|
UniValue localAddresses(UniValue::VARR);
|
|
650
649
|
{
|
|
651
650
|
LOCK(g_maplocalhost_mutex);
|
|
@@ -82,6 +82,8 @@ enum RPCErrorCode
|
|
|
82
82
|
RPC_WALLET_ALREADY_LOADED = -35, //!< This same wallet is already loaded
|
|
83
83
|
RPC_WALLET_ALREADY_EXISTS = -36, //!< There is already a wallet with the same name
|
|
84
84
|
|
|
85
|
+
//! peercoin
|
|
86
|
+
RPC_INSUFFICIENT_SEND_AMOUNT = -101, //! Transaction output is below minimum
|
|
85
87
|
//! Backwards compatible aliases
|
|
86
88
|
RPC_WALLET_INVALID_ACCOUNT_NAME = RPC_WALLET_INVALID_LABEL_NAME,
|
|
87
89
|
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
#include <node/transaction.h>
|
|
20
20
|
#include <policy/packages.h>
|
|
21
21
|
#include <policy/policy.h>
|
|
22
|
-
#include <policy/rbf.h>
|
|
23
22
|
#include <primitives/transaction.h>
|
|
24
23
|
#include <psbt.h>
|
|
25
24
|
#include <random.h>
|
|
@@ -47,7 +46,6 @@
|
|
|
47
46
|
|
|
48
47
|
using node::AnalyzePSBT;
|
|
49
48
|
using node::BroadcastTransaction;
|
|
50
|
-
using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
|
|
51
49
|
using node::FindCoins;
|
|
52
50
|
using node::GetTransaction;
|
|
53
51
|
using node::NodeContext;
|
|
@@ -71,7 +69,6 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
|
|
|
71
69
|
if (pindex) {
|
|
72
70
|
if (active_chainstate.m_chain.Contains(pindex)) {
|
|
73
71
|
entry.pushKV("confirmations", 1 + active_chainstate.m_chain.Height() - pindex->nHeight);
|
|
74
|
-
entry.pushKV("time", pindex->GetBlockTime());
|
|
75
72
|
entry.pushKV("blocktime", pindex->GetBlockTime());
|
|
76
73
|
}
|
|
77
74
|
else
|
|
@@ -122,7 +119,6 @@ static RPCHelpMan getrawtransaction()
|
|
|
122
119
|
return RPCHelpMan{
|
|
123
120
|
"getrawtransaction",
|
|
124
121
|
"\nReturn the raw transaction data.\n"
|
|
125
|
-
|
|
126
122
|
"\nBy default, this call only returns a transaction if it is in the mempool. If -txindex is enabled\n"
|
|
127
123
|
"and no blockhash argument is passed, it will return the transaction if it is in the mempool or any block.\n"
|
|
128
124
|
"If a blockhash argument is passed, it will return the transaction if\n"
|
|
@@ -182,7 +178,7 @@ static RPCHelpMan getrawtransaction()
|
|
|
182
178
|
{RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
|
|
183
179
|
{RPCResult::Type::STR, "hex", "the hex"},
|
|
184
180
|
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
|
|
185
|
-
{RPCResult::Type::STR, "address", /*optional=*/true, "The
|
|
181
|
+
{RPCResult::Type::STR, "address", /*optional=*/true, "The Peercoin address (only if a well-defined address exists)"},
|
|
186
182
|
}},
|
|
187
183
|
}},
|
|
188
184
|
}},
|
|
@@ -441,15 +437,11 @@ static RPCHelpMan createrawtransaction()
|
|
|
441
437
|
UniValue::VARR,
|
|
442
438
|
UniValueType(), // ARR or OBJ, checked later
|
|
443
439
|
UniValue::VNUM,
|
|
444
|
-
UniValue::
|
|
440
|
+
UniValue::VNUM
|
|
445
441
|
}, true
|
|
446
442
|
);
|
|
447
443
|
|
|
448
|
-
|
|
449
|
-
if (!request.params[3].isNull()) {
|
|
450
|
-
rbf = request.params[3].isTrue();
|
|
451
|
-
}
|
|
452
|
-
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
|
|
444
|
+
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
|
|
453
445
|
|
|
454
446
|
return EncodeHexTx(CTransaction(rawTx));
|
|
455
447
|
},
|
|
@@ -511,7 +503,7 @@ static RPCHelpMan decoderawtransaction()
|
|
|
511
503
|
{RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
|
|
512
504
|
{RPCResult::Type::STR_HEX, "hex", "the hex"},
|
|
513
505
|
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
|
|
514
|
-
{RPCResult::Type::STR, "address", /*optional=*/true, "The
|
|
506
|
+
{RPCResult::Type::STR, "address", /*optional=*/true, "The Peercoin address (only if a well-defined address exists)"},
|
|
515
507
|
}},
|
|
516
508
|
}},
|
|
517
509
|
}},
|
|
@@ -556,7 +548,7 @@ static RPCHelpMan decodescript()
|
|
|
556
548
|
{RPCResult::Type::STR, "asm", "Script public key"},
|
|
557
549
|
{RPCResult::Type::STR, "desc", "Inferred descriptor for the script"},
|
|
558
550
|
{RPCResult::Type::STR, "type", "The output type (e.g. " + GetAllOutputTypes() + ")"},
|
|
559
|
-
{RPCResult::Type::STR, "address", /*optional=*/true, "The
|
|
551
|
+
{RPCResult::Type::STR, "address", /*optional=*/true, "The Peercoin address (only if a well-defined address exists)"},
|
|
560
552
|
{RPCResult::Type::STR, "p2sh", /*optional=*/true,
|
|
561
553
|
"address of P2SH script wrapping this redeem script (not returned for types that should not be wrapped)"},
|
|
562
554
|
{RPCResult::Type::OBJ, "segwit", /*optional=*/true,
|
|
@@ -846,7 +838,6 @@ static RPCHelpMan signrawtransactionwithkey()
|
|
|
846
838
|
keystore.AddKey(key);
|
|
847
839
|
}
|
|
848
840
|
|
|
849
|
-
// Fetch previous transactions (inputs):
|
|
850
841
|
std::map<COutPoint, Coin> coins;
|
|
851
842
|
for (const CTxIn& txin : mtx.vin) {
|
|
852
843
|
coins[txin.prevout]; // Create empty map entry keyed by prevout.
|
|
@@ -875,9 +866,6 @@ static RPCHelpMan sendrawtransaction()
|
|
|
875
866
|
"\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",
|
|
876
867
|
{
|
|
877
868
|
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
|
|
878
|
-
{"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
|
|
879
|
-
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
|
|
880
|
-
"/kvB.\nSet to 0 to accept any fee rate.\n"},
|
|
881
869
|
},
|
|
882
870
|
RPCResult{
|
|
883
871
|
RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
|
|
@@ -896,7 +884,6 @@ static RPCHelpMan sendrawtransaction()
|
|
|
896
884
|
{
|
|
897
885
|
RPCTypeCheck(request.params, {
|
|
898
886
|
UniValue::VSTR,
|
|
899
|
-
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
|
|
900
887
|
});
|
|
901
888
|
|
|
902
889
|
CMutableTransaction mtx;
|
|
@@ -905,17 +892,10 @@ static RPCHelpMan sendrawtransaction()
|
|
|
905
892
|
}
|
|
906
893
|
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
|
|
907
894
|
|
|
908
|
-
const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
|
|
909
|
-
DEFAULT_MAX_RAW_TX_FEE_RATE :
|
|
910
|
-
CFeeRate(AmountFromValue(request.params[1]));
|
|
911
|
-
|
|
912
|
-
int64_t virtual_size = GetVirtualTransactionSize(*tx);
|
|
913
|
-
CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
|
|
914
|
-
|
|
915
895
|
std::string err_string;
|
|
916
896
|
AssertLockNotHeld(cs_main);
|
|
917
897
|
NodeContext& node = EnsureAnyNodeContext(request.context);
|
|
918
|
-
const TransactionError err = BroadcastTransaction(node, tx, err_string,
|
|
898
|
+
const TransactionError err = BroadcastTransaction(node, tx, err_string, /*relay*/ true, /*wait_callback*/ true);
|
|
919
899
|
if (TransactionError::OK != err) {
|
|
920
900
|
throw JSONRPCTransactionError(err, err_string);
|
|
921
901
|
}
|
|
@@ -940,8 +920,6 @@ static RPCHelpMan testmempoolaccept()
|
|
|
940
920
|
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
|
|
941
921
|
},
|
|
942
922
|
},
|
|
943
|
-
{"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
|
|
944
|
-
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kvB\n"},
|
|
945
923
|
},
|
|
946
924
|
RPCResult{
|
|
947
925
|
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
|
|
@@ -978,7 +956,6 @@ static RPCHelpMan testmempoolaccept()
|
|
|
978
956
|
{
|
|
979
957
|
RPCTypeCheck(request.params, {
|
|
980
958
|
UniValue::VARR,
|
|
981
|
-
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
|
|
982
959
|
});
|
|
983
960
|
const UniValue raw_transactions = request.params[0].get_array();
|
|
984
961
|
if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
|
|
@@ -986,10 +963,6 @@ static RPCHelpMan testmempoolaccept()
|
|
|
986
963
|
"Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
|
|
987
964
|
}
|
|
988
965
|
|
|
989
|
-
const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
|
|
990
|
-
DEFAULT_MAX_RAW_TX_FEE_RATE :
|
|
991
|
-
CFeeRate(AmountFromValue(request.params[1]));
|
|
992
|
-
|
|
993
966
|
std::vector<CTransactionRef> txns;
|
|
994
967
|
txns.reserve(raw_transactions.size());
|
|
995
968
|
for (const auto& rawtx : raw_transactions.getValues()) {
|
|
@@ -1038,20 +1011,13 @@ static RPCHelpMan testmempoolaccept()
|
|
|
1038
1011
|
const CAmount fee = tx_result.m_base_fees.value();
|
|
1039
1012
|
// Check that fee does not exceed maximum fee
|
|
1040
1013
|
const int64_t virtual_size = tx_result.m_vsize.value();
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
// These can be used to calculate the feerate.
|
|
1049
|
-
result_inner.pushKV("allowed", true);
|
|
1050
|
-
result_inner.pushKV("vsize", virtual_size);
|
|
1051
|
-
UniValue fees(UniValue::VOBJ);
|
|
1052
|
-
fees.pushKV("base", ValueFromAmount(fee));
|
|
1053
|
-
result_inner.pushKV("fees", fees);
|
|
1054
|
-
}
|
|
1014
|
+
// Only return the fee and vsize if the transaction would pass ATMP.
|
|
1015
|
+
// These can be used to calculate the feerate.
|
|
1016
|
+
result_inner.pushKV("allowed", true);
|
|
1017
|
+
result_inner.pushKV("vsize", virtual_size);
|
|
1018
|
+
UniValue fees(UniValue::VOBJ);
|
|
1019
|
+
fees.pushKV("base", ValueFromAmount(fee));
|
|
1020
|
+
result_inner.pushKV("fees", fees);
|
|
1055
1021
|
} else {
|
|
1056
1022
|
result_inner.pushKV("allowed", false);
|
|
1057
1023
|
const TxValidationState state = tx_result.m_state;
|
|
@@ -1072,7 +1038,7 @@ static RPCHelpMan decodepsbt()
|
|
|
1072
1038
|
{
|
|
1073
1039
|
return RPCHelpMan{
|
|
1074
1040
|
"decodepsbt",
|
|
1075
|
-
"Return a JSON object representing the serialized, base64-encoded partially signed
|
|
1041
|
+
"Return a JSON object representing the serialized, base64-encoded partially signed Peercoin transaction.",
|
|
1076
1042
|
{
|
|
1077
1043
|
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The PSBT base64 string"},
|
|
1078
1044
|
},
|
|
@@ -1124,7 +1090,7 @@ static RPCHelpMan decodepsbt()
|
|
|
1124
1090
|
{RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
|
|
1125
1091
|
{RPCResult::Type::STR_HEX, "hex", "The hex"},
|
|
1126
1092
|
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
|
|
1127
|
-
{RPCResult::Type::STR, "address", /*optional=*/true, "The
|
|
1093
|
+
{RPCResult::Type::STR, "address", /*optional=*/true, "The Peercoin address (only if a well-defined address exists)"},
|
|
1128
1094
|
}},
|
|
1129
1095
|
}},
|
|
1130
1096
|
{RPCResult::Type::OBJ_DYN, "partial_signatures", /*optional=*/true, "",
|
|
@@ -1537,7 +1503,7 @@ static RPCHelpMan decodepsbt()
|
|
|
1537
1503
|
static RPCHelpMan combinepsbt()
|
|
1538
1504
|
{
|
|
1539
1505
|
return RPCHelpMan{"combinepsbt",
|
|
1540
|
-
"\nCombine multiple partially signed
|
|
1506
|
+
"\nCombine multiple partially signed Peercoin transactions into one transaction.\n"
|
|
1541
1507
|
"Implements the Combiner role.\n",
|
|
1542
1508
|
{
|
|
1543
1509
|
{"txs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The base64 strings of partially signed transactions",
|
|
@@ -1662,15 +1628,11 @@ static RPCHelpMan createpsbt()
|
|
|
1662
1628
|
UniValue::VARR,
|
|
1663
1629
|
UniValueType(), // ARR or OBJ, checked later
|
|
1664
1630
|
UniValue::VNUM,
|
|
1665
|
-
UniValue::
|
|
1631
|
+
UniValue::VNUM,
|
|
1666
1632
|
}, true
|
|
1667
1633
|
);
|
|
1668
1634
|
|
|
1669
|
-
|
|
1670
|
-
if (!request.params[3].isNull()) {
|
|
1671
|
-
rbf = request.params[3].isTrue();
|
|
1672
|
-
}
|
|
1673
|
-
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
|
|
1635
|
+
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
|
|
1674
1636
|
|
|
1675
1637
|
// Make a blank psbt
|
|
1676
1638
|
PartiallySignedTransaction psbtx;
|
|
@@ -2052,9 +2014,6 @@ static RPCHelpMan analyzepsbt()
|
|
|
2052
2014
|
if (psbta.estimated_vsize != std::nullopt) {
|
|
2053
2015
|
result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize);
|
|
2054
2016
|
}
|
|
2055
|
-
if (psbta.estimated_feerate != std::nullopt) {
|
|
2056
|
-
result.pushKV("estimated_feerate", ValueFromAmount(psbta.estimated_feerate->GetFeePerK()));
|
|
2057
|
-
}
|
|
2058
2017
|
if (psbta.fee != std::nullopt) {
|
|
2059
2018
|
result.pushKV("fee", ValueFromAmount(*psbta.fee));
|
|
2060
2019
|
}
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
#include <script/signingprovider.h>
|
|
18
18
|
#include <tinyformat.h>
|
|
19
19
|
#include <univalue.h>
|
|
20
|
-
#include <util/rbf.h>
|
|
21
20
|
#include <util/strencodings.h>
|
|
21
|
+
#include <util/system.h>
|
|
22
22
|
#include <util/translation.h>
|
|
23
23
|
|
|
24
|
-
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime,
|
|
24
|
+
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& timestamp)
|
|
25
25
|
{
|
|
26
26
|
if (outputs_in.isNull()) {
|
|
27
27
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
|
|
@@ -38,6 +38,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|
|
38
38
|
UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
|
|
39
39
|
|
|
40
40
|
CMutableTransaction rawTx;
|
|
41
|
+
rawTx.nVersion = std::stoi(gArgs.GetArg("-txversion", std::to_string(CTransaction::CURRENT_VERSION)));
|
|
41
42
|
|
|
42
43
|
if (!locktime.isNull()) {
|
|
43
44
|
int64_t nLockTime = locktime.get_int64();
|
|
@@ -46,9 +47,17 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|
|
46
47
|
rawTx.nLockTime = nLockTime;
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
if (!timestamp.isNull()) {
|
|
51
|
+
int64_t nTime = timestamp.get_int64();
|
|
52
|
+
if (nTime < 0 || nTime > LOCKTIME_MAX)
|
|
53
|
+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, timestamp out of range");
|
|
54
|
+
rawTx.nTime = nTime;
|
|
55
|
+
}
|
|
56
|
+
|
|
49
57
|
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
|
|
50
58
|
const UniValue& input = inputs[idx];
|
|
51
59
|
const UniValue& o = input.get_obj();
|
|
60
|
+
CScript scriptSig;
|
|
52
61
|
|
|
53
62
|
uint256 txid = ParseHashO(o, "txid");
|
|
54
63
|
|
|
@@ -60,9 +69,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|
|
60
69
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
|
|
61
70
|
|
|
62
71
|
uint32_t nSequence;
|
|
63
|
-
if (
|
|
64
|
-
nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
|
|
65
|
-
} else if (rawTx.nLockTime) {
|
|
72
|
+
if (rawTx.nLockTime) {
|
|
66
73
|
nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; /* CTxIn::SEQUENCE_FINAL - 1 */
|
|
67
74
|
} else {
|
|
68
75
|
nSequence = CTxIn::SEQUENCE_FINAL;
|
|
@@ -79,7 +86,15 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|
|
79
86
|
}
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
|
|
89
|
+
// set redeem script
|
|
90
|
+
const UniValue& rs = find_value(o, "redeemScript");
|
|
91
|
+
if (!rs.isNull()) {
|
|
92
|
+
std::vector<unsigned char> redeemScriptData(ParseHex(rs.getValStr()));
|
|
93
|
+
CScript redeemScript(redeemScriptData.begin(), redeemScriptData.end());
|
|
94
|
+
scriptSig = redeemScript;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
CTxIn in(COutPoint(txid, nOutput), scriptSig, nSequence);
|
|
83
98
|
|
|
84
99
|
rawTx.vin.push_back(in);
|
|
85
100
|
}
|
|
@@ -114,10 +129,19 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|
|
114
129
|
|
|
115
130
|
CTxOut out(0, CScript() << OP_RETURN << data);
|
|
116
131
|
rawTx.vout.push_back(out);
|
|
132
|
+
} else if (name_ == "coinstake") {
|
|
133
|
+
rawTx.nVersion = 1;
|
|
134
|
+
rawTx.vout.push_back(CTxOut(0, CScript()));
|
|
117
135
|
} else {
|
|
118
136
|
CTxDestination destination = DecodeDestination(name_);
|
|
119
137
|
if (!IsValidDestination(destination)) {
|
|
120
|
-
|
|
138
|
+
if(name_.rfind("pubkey:",0) == 0) {
|
|
139
|
+
CScript scriptPubKey = CScript() << ToByteVector(ParseHex(name_.substr(7))) << OP_CHECKSIG;
|
|
140
|
+
CTxOut out(AmountFromValue(outputs[name_]), scriptPubKey);
|
|
141
|
+
rawTx.vout.push_back(out);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Peercoin address: ") + name_);
|
|
121
145
|
}
|
|
122
146
|
|
|
123
147
|
if (!destinations.insert(destination).second) {
|
|
@@ -132,10 +156,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|
|
132
156
|
}
|
|
133
157
|
}
|
|
134
158
|
|
|
135
|
-
if (rbf && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) {
|
|
136
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
|
|
137
|
-
}
|
|
138
|
-
|
|
139
159
|
return rawTx;
|
|
140
160
|
}
|
|
141
161
|
|
|
@@ -38,6 +38,6 @@ void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const
|
|
|
38
38
|
void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins);
|
|
39
39
|
|
|
40
40
|
/** Create a transaction from univalue parameters */
|
|
41
|
-
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime,
|
|
41
|
+
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& timestamp);
|
|
42
42
|
|
|
43
43
|
#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
#include <net_processing.h>
|
|
8
8
|
#include <node/context.h>
|
|
9
|
-
#include <policy/fees.h>
|
|
10
9
|
#include <rpc/protocol.h>
|
|
11
10
|
#include <rpc/request.h>
|
|
12
11
|
#include <txmempool.h>
|
|
@@ -65,19 +64,6 @@ ChainstateManager& EnsureAnyChainman(const std::any& context)
|
|
|
65
64
|
return EnsureChainman(EnsureAnyNodeContext(context));
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node)
|
|
69
|
-
{
|
|
70
|
-
if (!node.fee_estimator) {
|
|
71
|
-
throw JSONRPCError(RPC_INTERNAL_ERROR, "Fee estimation disabled");
|
|
72
|
-
}
|
|
73
|
-
return *node.fee_estimator;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context)
|
|
77
|
-
{
|
|
78
|
-
return EnsureFeeEstimator(EnsureAnyNodeContext(context));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
67
|
CConnman& EnsureConnman(const NodeContext& node)
|
|
82
68
|
{
|
|
83
69
|
if (!node.connman) {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
#include <boost/algorithm/string/split.hpp>
|
|
20
20
|
|
|
21
21
|
const std::string UNIX_EPOCH_TIME = "UNIX epoch time";
|
|
22
|
-
const std::string EXAMPLE_ADDRESS[2] = {"
|
|
22
|
+
const std::string EXAMPLE_ADDRESS[2] = {"pc1q084czy2a0gnnahmn0s8xewhdzysqgxd5rm35uc", "pc1q8hjye3kge69kdqj0ev63myg9d9jnnpsj9tejq2"};
|
|
23
23
|
|
|
24
24
|
std::string GetAllOutputTypes()
|
|
25
25
|
{
|
|
@@ -165,7 +165,7 @@ std::string ShellQuoteIfNeeded(const std::string& s)
|
|
|
165
165
|
|
|
166
166
|
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
|
|
167
167
|
{
|
|
168
|
-
return ">
|
|
168
|
+
return "> peercoin-cli " + methodname + " " + args + "\n";
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args)
|
|
@@ -91,7 +91,7 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
|
|
|
91
91
|
* @param[in] decimals Number of significant digits (default: 8).
|
|
92
92
|
* @returns a CAmount if the various checks pass.
|
|
93
93
|
*/
|
|
94
|
-
CAmount AmountFromValue(const UniValue& value, int decimals =
|
|
94
|
+
CAmount AmountFromValue(const UniValue& value, int decimals = 6);
|
|
95
95
|
|
|
96
96
|
using RPCArgList = std::vector<std::pair<std::string, UniValue>>;
|
|
97
97
|
std::string HelpExampleCli(const std::string& methodname, const std::string& args);
|
|
@@ -1348,6 +1348,10 @@ public:
|
|
|
1348
1348
|
void Serialize(S &s) const {
|
|
1349
1349
|
// Serialize nVersion
|
|
1350
1350
|
::Serialize(s, txTo.nVersion);
|
|
1351
|
+
if (txTo.nVersion < 3) {
|
|
1352
|
+
// Serialize nTime
|
|
1353
|
+
::Serialize(s, txTo.nTime);
|
|
1354
|
+
}
|
|
1351
1355
|
// Serialize vin
|
|
1352
1356
|
unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size();
|
|
1353
1357
|
::WriteCompactSize(s, nInputs);
|
|
@@ -1620,6 +1624,10 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|
|
1620
1624
|
CHashWriter ss(SER_GETHASH, 0);
|
|
1621
1625
|
// Version
|
|
1622
1626
|
ss << txTo.nVersion;
|
|
1627
|
+
if (txTo.nVersion < 3) {
|
|
1628
|
+
// nTime
|
|
1629
|
+
ss << txTo.nTime;
|
|
1630
|
+
}
|
|
1623
1631
|
// Input prevouts/nSequence (none/all, depending on flags)
|
|
1624
1632
|
ss << hashPrevouts;
|
|
1625
1633
|
ss << hashSequence;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
#include <util/strencodings.h>
|
|
9
9
|
|
|
10
|
+
#include <key.h>
|
|
10
11
|
#include <string>
|
|
11
12
|
|
|
12
13
|
std::string GetOpName(opcodetype opcode)
|
|
@@ -280,6 +281,19 @@ bool CScript::HasValidOps() const
|
|
|
280
281
|
return true;
|
|
281
282
|
}
|
|
282
283
|
|
|
284
|
+
void CScript::SetMultisig(int nRequired, const std::vector<CPubKey>& keys)
|
|
285
|
+
{
|
|
286
|
+
this->clear();
|
|
287
|
+
|
|
288
|
+
*this << EncodeOP_N(nRequired);
|
|
289
|
+
for (const auto& pubkey : keys)
|
|
290
|
+
{
|
|
291
|
+
std::vector<unsigned char> vchPubKey(pubkey.begin(), pubkey.end());
|
|
292
|
+
*this << vchPubKey;
|
|
293
|
+
}
|
|
294
|
+
*this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
|
|
295
|
+
}
|
|
296
|
+
|
|
283
297
|
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet)
|
|
284
298
|
{
|
|
285
299
|
opcodeRet = OP_INVALIDOPCODE;
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
#include <string>
|
|
21
21
|
#include <vector>
|
|
22
22
|
|
|
23
|
+
class CPubKey;
|
|
24
|
+
|
|
23
25
|
// Maximum number of bytes pushable to the stack
|
|
24
26
|
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520;
|
|
25
27
|
|
|
@@ -223,7 +225,7 @@ class CScriptNum
|
|
|
223
225
|
* The semantics are subtle, though: operands must be in the range [-2^31 +1...2^31 -1],
|
|
224
226
|
* but results may overflow (and are valid as long as they are not used in a subsequent
|
|
225
227
|
* numeric operation). CScriptNum enforces those semantics by storing results as
|
|
226
|
-
* an
|
|
228
|
+
* an int64_t and allowing out-of-range values to be returned as a vector of bytes but
|
|
227
229
|
* throwing an exception if arithmetic is done or the result is interpreted as an integer.
|
|
228
230
|
*/
|
|
229
231
|
public:
|
|
@@ -552,6 +554,8 @@ public:
|
|
|
552
554
|
CScriptBase::clear();
|
|
553
555
|
shrink_to_fit();
|
|
554
556
|
}
|
|
557
|
+
|
|
558
|
+
void SetMultisig(int nRequired, const std::vector<CPubKey>& keys);
|
|
555
559
|
};
|
|
556
560
|
|
|
557
561
|
struct CScriptWitness
|
|
@@ -622,6 +622,10 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
|
|
|
622
622
|
{
|
|
623
623
|
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
|
624
624
|
|
|
625
|
+
// we don't need nTime anymore
|
|
626
|
+
if (mtx.nVersion >= 3)
|
|
627
|
+
mtx.nTime = 0;
|
|
628
|
+
|
|
625
629
|
// Use CTransaction for the constant parts of the
|
|
626
630
|
// transaction to avoid rehashing.
|
|
627
631
|
const CTransaction txConst(mtx);
|
|
@@ -32,10 +32,10 @@ public:
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Default setting for nMaxDatacarrierBytes.
|
|
35
|
+
* Default setting for nMaxDatacarrierBytes. 256 bytes of data, +1 for OP_RETURN,
|
|
36
36
|
* +2 for the pushdata opcodes.
|
|
37
37
|
*/
|
|
38
|
-
static const unsigned int MAX_OP_RETURN_RELAY =
|
|
38
|
+
static const unsigned int MAX_OP_RETURN_RELAY = 259;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* A data carrying output is an unspendable output containing data. The script
|
|
@@ -131,6 +131,8 @@ enum
|
|
|
131
131
|
SER_NETWORK = (1 << 0),
|
|
132
132
|
SER_DISK = (1 << 1),
|
|
133
133
|
SER_GETHASH = (1 << 2),
|
|
134
|
+
|
|
135
|
+
SER_POSMARKER = (1 << 18), // peercoin: for sending block headers with PoS marker, to allow headers-first syncronization
|
|
134
136
|
};
|
|
135
137
|
|
|
136
138
|
//! Convert the reference base type to X, without changing constness or reference type.
|
|
@@ -984,9 +986,10 @@ class CSizeComputer
|
|
|
984
986
|
protected:
|
|
985
987
|
size_t nSize;
|
|
986
988
|
|
|
989
|
+
const int nType;
|
|
987
990
|
const int nVersion;
|
|
988
991
|
public:
|
|
989
|
-
explicit CSizeComputer(int nVersionIn) : nSize(0), nVersion(nVersionIn) {}
|
|
992
|
+
explicit CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {}
|
|
990
993
|
|
|
991
994
|
void write(Span<const std::byte> src)
|
|
992
995
|
{
|
|
@@ -1010,6 +1013,7 @@ public:
|
|
|
1010
1013
|
return nSize;
|
|
1011
1014
|
}
|
|
1012
1015
|
|
|
1016
|
+
int GetType() const { return nType; }
|
|
1013
1017
|
int GetVersion() const { return nVersion; }
|
|
1014
1018
|
};
|
|
1015
1019
|
|
|
@@ -1083,15 +1087,15 @@ inline void WriteCompactSize(CSizeComputer &s, uint64_t nSize)
|
|
|
1083
1087
|
}
|
|
1084
1088
|
|
|
1085
1089
|
template <typename T>
|
|
1086
|
-
size_t GetSerializeSize(const T& t, int nVersion = 0)
|
|
1090
|
+
size_t GetSerializeSize(const T& t, int nType, int nVersion = 0)
|
|
1087
1091
|
{
|
|
1088
|
-
return (CSizeComputer(nVersion) << t).size();
|
|
1092
|
+
return (CSizeComputer(nType, nVersion) << t).size();
|
|
1089
1093
|
}
|
|
1090
1094
|
|
|
1091
1095
|
template <typename... T>
|
|
1092
1096
|
size_t GetSerializeSizeMany(int nVersion, const T&... t)
|
|
1093
1097
|
{
|
|
1094
|
-
CSizeComputer sc(nVersion);
|
|
1098
|
+
CSizeComputer sc(0, nVersion);
|
|
1095
1099
|
SerializeMany(sc, t...);
|
|
1096
1100
|
return sc.size();
|
|
1097
1101
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#include <stdint.h>
|
|
2
|
+
|
|
3
|
+
// needed when linking transaction.cpp, since we are not going to pull real GetAdjustedTime from timedata.cpp
|
|
4
|
+
int64_t GetAdjustedTime()
|
|
5
|
+
{
|
|
6
|
+
return 0;
|
|
7
|
+
}
|
|
@@ -322,7 +322,16 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
|
|
|
322
322
|
pindexNew->nStatus = diskindex.nStatus;
|
|
323
323
|
pindexNew->nTx = diskindex.nTx;
|
|
324
324
|
|
|
325
|
-
|
|
325
|
+
// peercoin related block index fields
|
|
326
|
+
pindexNew->nMint = diskindex.nMint;
|
|
327
|
+
pindexNew->nMoneySupply = diskindex.nMoneySupply;
|
|
328
|
+
pindexNew->nFlags = diskindex.nFlags;
|
|
329
|
+
pindexNew->nStakeModifier = diskindex.nStakeModifier;
|
|
330
|
+
pindexNew->prevoutStake = diskindex.prevoutStake;
|
|
331
|
+
pindexNew->nStakeTime = diskindex.nStakeTime;
|
|
332
|
+
pindexNew->hashProofOfStake = diskindex.hashProofOfStake;
|
|
333
|
+
|
|
334
|
+
if (pindexNew->IsProofOfWork() && !CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams)) {
|
|
326
335
|
return error("%s: CheckProofOfWork failed: %s", __func__, pindexNew->ToString());
|
|
327
336
|
}
|
|
328
337
|
|
|
@@ -353,6 +362,12 @@ public:
|
|
|
353
362
|
//! at which height this transaction was included in the active block chain
|
|
354
363
|
int nHeight;
|
|
355
364
|
|
|
365
|
+
// peercoin: whether transaction is a coinstake
|
|
366
|
+
bool fCoinStake;
|
|
367
|
+
|
|
368
|
+
// peercoin: transaction timestamp
|
|
369
|
+
unsigned int nTime;
|
|
370
|
+
|
|
356
371
|
//! empty constructor
|
|
357
372
|
CCoins() : fCoinBase(false), vout(0), nHeight(0) { }
|
|
358
373
|
|
|
@@ -435,7 +450,7 @@ bool CCoinsViewDB::Upgrade() {
|
|
|
435
450
|
COutPoint outpoint(key.second, 0);
|
|
436
451
|
for (size_t i = 0; i < old_coins.vout.size(); ++i) {
|
|
437
452
|
if (!old_coins.vout[i].IsNull() && !old_coins.vout[i].scriptPubKey.IsUnspendable()) {
|
|
438
|
-
Coin newcoin(std::move(old_coins.vout[i]), old_coins.nHeight, old_coins.fCoinBase);
|
|
453
|
+
Coin newcoin(std::move(old_coins.vout[i]), old_coins.nHeight, old_coins.fCoinBase, old_coins.fCoinStake, old_coins.nTime);
|
|
439
454
|
outpoint.n = i;
|
|
440
455
|
CoinEntry entry(&outpoint);
|
|
441
456
|
batch.Write(entry, newcoin);
|
|
@@ -10,15 +10,17 @@
|
|
|
10
10
|
#include <consensus/consensus.h>
|
|
11
11
|
#include <consensus/tx_verify.h>
|
|
12
12
|
#include <consensus/validation.h>
|
|
13
|
-
#include <
|
|
13
|
+
#include <kernel.h>
|
|
14
14
|
#include <policy/policy.h>
|
|
15
15
|
#include <policy/settings.h>
|
|
16
16
|
#include <reverse_iterator.h>
|
|
17
|
+
#include <timedata.h>
|
|
17
18
|
#include <util/moneystr.h>
|
|
18
19
|
#include <util/system.h>
|
|
19
20
|
#include <util/time.h>
|
|
20
21
|
#include <validationinterface.h>
|
|
21
22
|
|
|
23
|
+
#include <chainparams.h>
|
|
22
24
|
#include <cmath>
|
|
23
25
|
#include <optional>
|
|
24
26
|
|
|
@@ -461,8 +463,8 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee,
|
|
|
461
463
|
assert(int(nSigOpCostWithAncestors) >= 0);
|
|
462
464
|
}
|
|
463
465
|
|
|
464
|
-
CTxMemPool::CTxMemPool(
|
|
465
|
-
: m_check_ratio(check_ratio)
|
|
466
|
+
CTxMemPool::CTxMemPool(int check_ratio)
|
|
467
|
+
: m_check_ratio(check_ratio)
|
|
466
468
|
{
|
|
467
469
|
_clear(); //lock free clear
|
|
468
470
|
}
|
|
@@ -483,7 +485,7 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n)
|
|
|
483
485
|
nTransactionsUpdated += n;
|
|
484
486
|
}
|
|
485
487
|
|
|
486
|
-
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAncestors
|
|
488
|
+
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAncestors)
|
|
487
489
|
{
|
|
488
490
|
// Add to memory pool without checking anything.
|
|
489
491
|
// Used by AcceptToMemoryPool(), which DOES do
|
|
@@ -526,10 +528,6 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
|
|
|
526
528
|
|
|
527
529
|
nTransactionsUpdated++;
|
|
528
530
|
totalTxSize += entry.GetTxSize();
|
|
529
|
-
m_total_fee += entry.GetFee();
|
|
530
|
-
if (minerPolicyEstimator) {
|
|
531
|
-
minerPolicyEstimator->processTransaction(entry, validFeeEstimate);
|
|
532
|
-
}
|
|
533
531
|
|
|
534
532
|
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
|
|
535
533
|
newit->vTxHashesIdx = vTxHashes.size() - 1;
|
|
@@ -549,12 +547,9 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
|
|
549
547
|
GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx(), reason, mempool_sequence);
|
|
550
548
|
}
|
|
551
549
|
|
|
552
|
-
const uint256 hash = it->GetTx().GetHash();
|
|
553
550
|
for (const CTxIn& txin : it->GetTx().vin)
|
|
554
551
|
mapNextTx.erase(txin.prevout);
|
|
555
552
|
|
|
556
|
-
RemoveUnbroadcastTx(hash, true /* add logging because unchecked */ );
|
|
557
|
-
|
|
558
553
|
if (vTxHashes.size() > 1) {
|
|
559
554
|
vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back());
|
|
560
555
|
vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx;
|
|
@@ -570,7 +565,6 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
|
|
570
565
|
cachedInnerUsage -= memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
|
|
571
566
|
mapTx.erase(it);
|
|
572
567
|
nTransactionsUpdated++;
|
|
573
|
-
if (minerPolicyEstimator) {minerPolicyEstimator->removeTx(hash, false);}
|
|
574
568
|
}
|
|
575
569
|
|
|
576
570
|
// Calculates descendants of entry that are not already in setDescendants, and adds to
|
|
@@ -685,8 +679,6 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
|
|
|
685
679
|
if (i != mapTx.end())
|
|
686
680
|
entries.push_back(&*i);
|
|
687
681
|
}
|
|
688
|
-
// Before the txs in the new block have been removed from the mempool, update policy estimates
|
|
689
|
-
if (minerPolicyEstimator) {minerPolicyEstimator->processBlock(nBlockHeight, entries);}
|
|
690
682
|
for (const auto& tx : vtx)
|
|
691
683
|
{
|
|
692
684
|
txiter it = mapTx.find(tx->GetHash());
|
|
@@ -698,8 +690,6 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
|
|
|
698
690
|
removeConflicts(*tx);
|
|
699
691
|
ClearPrioritisation(tx->GetHash());
|
|
700
692
|
}
|
|
701
|
-
lastRollingFeeUpdate = GetTime();
|
|
702
|
-
blockSinceLastRollingFeeBump = true;
|
|
703
693
|
}
|
|
704
694
|
|
|
705
695
|
void CTxMemPool::_clear()
|
|
@@ -709,9 +699,6 @@ void CTxMemPool::_clear()
|
|
|
709
699
|
totalTxSize = 0;
|
|
710
700
|
m_total_fee = 0;
|
|
711
701
|
cachedInnerUsage = 0;
|
|
712
|
-
lastRollingFeeUpdate = GetTime();
|
|
713
|
-
blockSinceLastRollingFeeBump = false;
|
|
714
|
-
rollingMinimumFeeRate = 0;
|
|
715
702
|
++nTransactionsUpdated;
|
|
716
703
|
}
|
|
717
704
|
|
|
@@ -812,9 +799,9 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
|
|
|
812
799
|
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
|
|
813
800
|
CAmount txfee = 0;
|
|
814
801
|
assert(!tx.IsCoinBase());
|
|
815
|
-
assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee));
|
|
802
|
+
assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee, Params().GetConsensus(), tx.nTime ? tx.nTime : GetAdjustedTime()));
|
|
816
803
|
for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout);
|
|
817
|
-
AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max());
|
|
804
|
+
AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max(), false, true);
|
|
818
805
|
}
|
|
819
806
|
for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) {
|
|
820
807
|
uint256 hash = it->second->GetHash();
|
|
@@ -1016,7 +1003,7 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
|
|
|
1016
1003
|
CTransactionRef ptx = mempool.get(outpoint.hash);
|
|
1017
1004
|
if (ptx) {
|
|
1018
1005
|
if (outpoint.n < ptx->vout.size()) {
|
|
1019
|
-
coin = Coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false);
|
|
1006
|
+
coin = Coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false, ptx->IsCoinStake(), ptx->nTime);
|
|
1020
1007
|
return true;
|
|
1021
1008
|
} else {
|
|
1022
1009
|
return false;
|
|
@@ -1028,7 +1015,7 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
|
|
|
1028
1015
|
void CCoinsViewMemPool::PackageAddTransaction(const CTransactionRef& tx)
|
|
1029
1016
|
{
|
|
1030
1017
|
for (unsigned int n = 0; n < tx->vout.size(); ++n) {
|
|
1031
|
-
m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false));
|
|
1018
|
+
m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false, tx->IsCoinStake(), tx->nTime));
|
|
1032
1019
|
}
|
|
1033
1020
|
}
|
|
1034
1021
|
|
|
@@ -1072,13 +1059,13 @@ int CTxMemPool::Expire(std::chrono::seconds time)
|
|
|
1072
1059
|
return stage.size();
|
|
1073
1060
|
}
|
|
1074
1061
|
|
|
1075
|
-
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry
|
|
1062
|
+
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry)
|
|
1076
1063
|
{
|
|
1077
1064
|
setEntries setAncestors;
|
|
1078
1065
|
uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
|
|
1079
1066
|
std::string dummy;
|
|
1080
1067
|
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy);
|
|
1081
|
-
return addUnchecked(entry, setAncestors
|
|
1068
|
+
return addUnchecked(entry, setAncestors);
|
|
1082
1069
|
}
|
|
1083
1070
|
|
|
1084
1071
|
void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add)
|
|
@@ -1103,55 +1090,13 @@ void CTxMemPool::UpdateParent(txiter entry, txiter parent, bool add)
|
|
|
1103
1090
|
}
|
|
1104
1091
|
}
|
|
1105
1092
|
|
|
1106
|
-
CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
|
|
1107
|
-
LOCK(cs);
|
|
1108
|
-
if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0)
|
|
1109
|
-
return CFeeRate(llround(rollingMinimumFeeRate));
|
|
1110
|
-
|
|
1111
|
-
int64_t time = GetTime();
|
|
1112
|
-
if (time > lastRollingFeeUpdate + 10) {
|
|
1113
|
-
double halflife = ROLLING_FEE_HALFLIFE;
|
|
1114
|
-
if (DynamicMemoryUsage() < sizelimit / 4)
|
|
1115
|
-
halflife /= 4;
|
|
1116
|
-
else if (DynamicMemoryUsage() < sizelimit / 2)
|
|
1117
|
-
halflife /= 2;
|
|
1118
|
-
|
|
1119
|
-
rollingMinimumFeeRate = rollingMinimumFeeRate / pow(2.0, (time - lastRollingFeeUpdate) / halflife);
|
|
1120
|
-
lastRollingFeeUpdate = time;
|
|
1121
|
-
|
|
1122
|
-
if (rollingMinimumFeeRate < (double)incrementalRelayFee.GetFeePerK() / 2) {
|
|
1123
|
-
rollingMinimumFeeRate = 0;
|
|
1124
|
-
return CFeeRate(0);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
return std::max(CFeeRate(llround(rollingMinimumFeeRate)), incrementalRelayFee);
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {
|
|
1131
|
-
AssertLockHeld(cs);
|
|
1132
|
-
if (rate.GetFeePerK() > rollingMinimumFeeRate) {
|
|
1133
|
-
rollingMinimumFeeRate = rate.GetFeePerK();
|
|
1134
|
-
blockSinceLastRollingFeeBump = false;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
1093
|
void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining) {
|
|
1139
1094
|
AssertLockHeld(cs);
|
|
1140
1095
|
|
|
1141
1096
|
unsigned nTxnRemoved = 0;
|
|
1142
|
-
CFeeRate maxFeeRateRemoved(0);
|
|
1143
1097
|
while (!mapTx.empty() && DynamicMemoryUsage() > sizelimit) {
|
|
1144
1098
|
indexed_transaction_set::index<descendant_score>::type::iterator it = mapTx.get<descendant_score>().begin();
|
|
1145
1099
|
|
|
1146
|
-
// We set the new mempool min fee to the feerate of the removed set, plus the
|
|
1147
|
-
// "minimum reasonable fee rate" (ie some value under which we consider txn
|
|
1148
|
-
// to have 0 fee). This way, we don't allow txn to enter mempool with feerate
|
|
1149
|
-
// equal to txn which were removed with no block in between.
|
|
1150
|
-
CFeeRate removed(it->GetModFeesWithDescendants(), it->GetSizeWithDescendants());
|
|
1151
|
-
removed += incrementalRelayFee;
|
|
1152
|
-
trackPackageRemoved(removed);
|
|
1153
|
-
maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed);
|
|
1154
|
-
|
|
1155
1100
|
setEntries stage;
|
|
1156
1101
|
CalculateDescendants(mapTx.project<0>(it), stage);
|
|
1157
1102
|
nTxnRemoved += stage.size();
|
|
@@ -1172,10 +1117,6 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpends
|
|
|
1172
1117
|
}
|
|
1173
1118
|
}
|
|
1174
1119
|
}
|
|
1175
|
-
|
|
1176
|
-
if (maxFeeRateRemoved > CFeeRate(0)) {
|
|
1177
|
-
LogPrint(BCLog::MEMPOOL, "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString());
|
|
1178
|
-
}
|
|
1179
1120
|
}
|
|
1180
1121
|
|
|
1181
1122
|
uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const {
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
#include <coins.h>
|
|
18
18
|
#include <consensus/amount.h>
|
|
19
19
|
#include <indirectmap.h>
|
|
20
|
-
#include <policy/feerate.h>
|
|
21
20
|
#include <policy/packages.h>
|
|
22
21
|
#include <primitives/transaction.h>
|
|
23
22
|
#include <random.h>
|
|
@@ -318,8 +317,6 @@ struct entry_time {};
|
|
|
318
317
|
struct ancestor_score {};
|
|
319
318
|
struct index_by_wtxid {};
|
|
320
319
|
|
|
321
|
-
class CBlockPolicyEstimator;
|
|
322
|
-
|
|
323
320
|
/**
|
|
324
321
|
* Information about a mempool transaction.
|
|
325
322
|
*/
|
|
@@ -361,9 +358,6 @@ enum class MemPoolRemovalReason {
|
|
|
361
358
|
* local node), but not all transactions seen are added to the pool. For
|
|
362
359
|
* example, the following new transactions will not be added to the mempool:
|
|
363
360
|
* - a transaction which doesn't meet the minimum fee requirements.
|
|
364
|
-
* - a new transaction that double-spends an input of a transaction already in
|
|
365
|
-
* the pool where the new transaction does not meet the Replace-By-Fee
|
|
366
|
-
* requirements as defined in BIP 125.
|
|
367
361
|
* - a non-standard transaction.
|
|
368
362
|
*
|
|
369
363
|
* CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping:
|
|
@@ -431,24 +425,17 @@ class CTxMemPool
|
|
|
431
425
|
protected:
|
|
432
426
|
const int m_check_ratio; //!< Value n means that 1 times in n we check.
|
|
433
427
|
std::atomic<unsigned int> nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation
|
|
434
|
-
CBlockPolicyEstimator* const minerPolicyEstimator;
|
|
435
428
|
|
|
436
429
|
uint64_t totalTxSize GUARDED_BY(cs); //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141.
|
|
437
430
|
CAmount m_total_fee GUARDED_BY(cs); //!< sum of all mempool tx's fees (NOT modified fee)
|
|
438
431
|
uint64_t cachedInnerUsage GUARDED_BY(cs); //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves)
|
|
439
432
|
|
|
440
|
-
mutable int64_t lastRollingFeeUpdate GUARDED_BY(cs);
|
|
441
|
-
mutable bool blockSinceLastRollingFeeBump GUARDED_BY(cs);
|
|
442
|
-
mutable double rollingMinimumFeeRate GUARDED_BY(cs); //!< minimum fee to get into the pool, decreases exponentially
|
|
443
433
|
mutable Epoch m_epoch GUARDED_BY(cs);
|
|
444
|
-
|
|
445
434
|
// In-memory counter for external mempool tracking purposes.
|
|
446
435
|
// This number is incremented once every time a transaction
|
|
447
436
|
// is added or removed from the mempool for any reason.
|
|
448
437
|
mutable uint64_t m_sequence_number GUARDED_BY(cs){1};
|
|
449
438
|
|
|
450
|
-
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
451
|
-
|
|
452
439
|
bool m_is_loaded GUARDED_BY(cs){false};
|
|
453
440
|
|
|
454
441
|
public:
|
|
@@ -568,7 +555,7 @@ public:
|
|
|
568
555
|
* @param[in] estimator is used to estimate appropriate transaction fees.
|
|
569
556
|
* @param[in] check_ratio is the ratio used to determine how often sanity checks will run.
|
|
570
557
|
*/
|
|
571
|
-
explicit CTxMemPool(
|
|
558
|
+
explicit CTxMemPool(int check_ratio = 0);
|
|
572
559
|
|
|
573
560
|
/**
|
|
574
561
|
* If sanity-checking is turned on, check makes sure the pool is
|
|
@@ -585,8 +572,8 @@ public:
|
|
|
585
572
|
// Note that addUnchecked is ONLY called from ATMP outside of tests
|
|
586
573
|
// and any other callers may break wallet's in-mempool tracking (due to
|
|
587
574
|
// lack of CValidationInterface::TransactionAddedToMempool callbacks).
|
|
588
|
-
void addUnchecked(const CTxMemPoolEntry& entry
|
|
589
|
-
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors
|
|
575
|
+
void addUnchecked(const CTxMemPoolEntry& entry) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
|
|
576
|
+
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
|
|
590
577
|
|
|
591
578
|
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
592
579
|
/** After reorg, filter the entries that would no longer be valid in the next block, and update
|
|
@@ -695,14 +682,6 @@ public:
|
|
|
695
682
|
* already in it. */
|
|
696
683
|
void CalculateDescendants(txiter it, setEntries& setDescendants) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
697
684
|
|
|
698
|
-
/** The minimum fee to get into the mempool, which may itself not be enough
|
|
699
|
-
* for larger-sized transactions.
|
|
700
|
-
* The incrementalRelayFee policy variable is used to bound the time it
|
|
701
|
-
* takes the fee rate to go back down all the way to 0. When the feerate
|
|
702
|
-
* would otherwise be half of this, it is set to 0 instead.
|
|
703
|
-
*/
|
|
704
|
-
CFeeRate GetMinFee(size_t sizelimit) const;
|
|
705
|
-
|
|
706
685
|
/** Remove transactions from the mempool until its dynamic size is <= sizelimit.
|
|
707
686
|
* pvNoSpendsRemaining, if set, will be populated with the list of outpoints
|
|
708
687
|
* which are not in mempool which no longer have any spends in this mempool.
|
|
@@ -22,6 +22,11 @@ protected:
|
|
|
22
22
|
static constexpr int WIDTH = BITS / 8;
|
|
23
23
|
uint8_t m_data[WIDTH];
|
|
24
24
|
public:
|
|
25
|
+
const uint32_t *GetDataPtr() const
|
|
26
|
+
{
|
|
27
|
+
return (const uint32_t *)m_data;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
/* construct 0 value by default */
|
|
26
31
|
constexpr base_blob() : m_data() {}
|
|
27
32
|
|
|
@@ -24,7 +24,8 @@ struct TxInUndoFormatter
|
|
|
24
24
|
{
|
|
25
25
|
template<typename Stream>
|
|
26
26
|
void Ser(Stream &s, const Coin& txout) {
|
|
27
|
-
::Serialize(s, VARINT(txout.nHeight * uint32_t{
|
|
27
|
+
::Serialize(s, VARINT(txout.nHeight * uint32_t{4} + txout.fCoinBase + (txout.fCoinStake ? 2u : 0u)));
|
|
28
|
+
::Serialize(s, VARINT(txout.nTime));
|
|
28
29
|
if (txout.nHeight > 0) {
|
|
29
30
|
// Required to maintain compatibility with older undo format.
|
|
30
31
|
::Serialize(s, (unsigned char)0);
|
|
@@ -36,8 +37,10 @@ struct TxInUndoFormatter
|
|
|
36
37
|
void Unser(Stream &s, Coin& txout) {
|
|
37
38
|
uint32_t nCode = 0;
|
|
38
39
|
::Unserialize(s, VARINT(nCode));
|
|
39
|
-
txout.nHeight = nCode >>
|
|
40
|
+
txout.nHeight = nCode >> 2;
|
|
40
41
|
txout.fCoinBase = nCode & 1;
|
|
42
|
+
txout.fCoinStake = nCode & 2;
|
|
43
|
+
::Unserialize(s, VARINT(txout.nTime));
|
|
41
44
|
if (txout.nHeight > 0) {
|
|
42
45
|
// Old versions stored the version number for the last spend of
|
|
43
46
|
// a transaction's outputs. Non-final spends were indicated with
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**********************************************************************
|
|
2
|
+
* Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko *
|
|
3
|
+
* Distributed under the MIT software license, see the accompanying *
|
|
4
|
+
* file LICENSE or http://www.opensource.org/licenses/mit-license.php.*
|
|
5
|
+
**********************************************************************/
|
|
6
|
+
|
|
7
|
+
#ifndef _MINISKETCH_UTIL_H_
|
|
8
|
+
#define _MINISKETCH_UTIL_H_
|
|
9
|
+
|
|
10
|
+
#ifdef MINISKETCH_VERIFY
|
|
11
|
+
#include <stdio.h>
|
|
12
|
+
#endif
|
|
13
|
+
|
|
14
|
+
#if !defined(__GNUC_PREREQ)
|
|
15
|
+
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
|
|
16
|
+
# define __GNUC_PREREQ(_maj,_min) \
|
|
17
|
+
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
|
|
18
|
+
# else
|
|
19
|
+
# define __GNUC_PREREQ(_maj,_min) 0
|
|
20
|
+
# endif
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
#if __GNUC_PREREQ(3, 0)
|
|
24
|
+
#define EXPECT(x,c) __builtin_expect((x),(c))
|
|
25
|
+
#else
|
|
26
|
+
#define EXPECT(x,c) (x)
|
|
27
|
+
#endif
|
|
28
|
+
|
|
29
|
+
/* Assertion macros */
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Unconditional failure on condition failure.
|
|
33
|
+
* Primarily used in testing harnesses.
|
|
34
|
+
*/
|
|
35
|
+
#define CHECK(cond) do { \
|
|
36
|
+
if (EXPECT(!(cond), 0)) { \
|
|
37
|
+
fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, "Check condition failed: " #cond); \
|
|
38
|
+
abort(); \
|
|
39
|
+
} \
|
|
40
|
+
} while(0)
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check macro that does nothing in normal non-verify builds but crashes in verify builds.
|
|
44
|
+
* This is used to test conditions at runtime that should always be true, but are either
|
|
45
|
+
* expensive to test or in locations where returning on failure would be messy.
|
|
46
|
+
*/
|
|
47
|
+
#ifdef MINISKETCH_VERIFY
|
|
48
|
+
#define CHECK_SAFE(cond) CHECK(cond)
|
|
49
|
+
#else
|
|
50
|
+
#define CHECK_SAFE(cond)
|
|
51
|
+
#endif
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check a condition and return on failure in non-verify builds, crash in verify builds.
|
|
55
|
+
* Used for inexpensive conditions which believed to be always true in locations where
|
|
56
|
+
* a graceful exit is possible.
|
|
57
|
+
*/
|
|
58
|
+
#ifdef MINISKETCH_VERIFY
|
|
59
|
+
#define CHECK_RETURN(cond, rvar) do { \
|
|
60
|
+
if (EXPECT(!(cond), 0)) { \
|
|
61
|
+
fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, "Check condition failed: " #cond); \
|
|
62
|
+
abort(); \
|
|
63
|
+
return rvar; /* Does nothing, but causes compile to warn on incorrect return types. */ \
|
|
64
|
+
} \
|
|
65
|
+
} while(0)
|
|
66
|
+
#else
|
|
67
|
+
#define CHECK_RETURN(cond, rvar) do { \
|
|
68
|
+
if (EXPECT(!(cond), 0)) { \
|
|
69
|
+
return rvar; \
|
|
70
|
+
} \
|
|
71
|
+
} while(0)
|
|
72
|
+
#endif
|
|
73
|
+
|
|
74
|
+
#endif
|
|
@@ -29,8 +29,6 @@ bilingual_str TransactionErrorString(const TransactionError err)
|
|
|
29
29
|
return Untranslated("PSBTs not compatible (different transactions)");
|
|
30
30
|
case TransactionError::SIGHASH_MISMATCH:
|
|
31
31
|
return Untranslated("Specified sighash value does not match value stored in PSBT");
|
|
32
|
-
case TransactionError::MAX_FEE_EXCEEDED:
|
|
33
|
-
return Untranslated("Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)");
|
|
34
32
|
case TransactionError::EXTERNAL_SIGNER_NOT_FOUND:
|
|
35
33
|
return Untranslated("External signer not found");
|
|
36
34
|
case TransactionError::EXTERNAL_SIGNER_FAILED:
|
|
@@ -29,7 +29,6 @@ enum class TransactionError {
|
|
|
29
29
|
INVALID_PSBT,
|
|
30
30
|
PSBT_MISMATCH,
|
|
31
31
|
SIGHASH_MISMATCH,
|
|
32
|
-
MAX_FEE_EXCEEDED,
|
|
33
32
|
EXTERNAL_SIGNER_NOT_FOUND,
|
|
34
33
|
EXTERNAL_SIGNER_FAILED,
|
|
35
34
|
};
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* Text used to signify that a signed message follows and to prevent
|
|
20
20
|
* inadvertently signing a transaction.
|
|
21
21
|
*/
|
|
22
|
-
const std::string MESSAGE_MAGIC = "
|
|
22
|
+
const std::string MESSAGE_MAGIC = "Peercoin Signed Message:\n";
|
|
23
23
|
|
|
24
24
|
MessageVerificationResult MessageVerify(
|
|
25
25
|
const std::string& address,
|
|
@@ -23,7 +23,7 @@ std::string FormatMoney(const CAmount n)
|
|
|
23
23
|
quotient = -quotient;
|
|
24
24
|
remainder = -remainder;
|
|
25
25
|
}
|
|
26
|
-
std::string str = strprintf("%d.%
|
|
26
|
+
std::string str = strprintf("%d.%06d", quotient, remainder);
|
|
27
27
|
|
|
28
28
|
// Right-trim excess zeros before the decimal point:
|
|
29
29
|
int nTrim = 0;
|
|
@@ -426,13 +426,17 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
|
|
|
426
426
|
if (ptr < end && val[ptr] == '.')
|
|
427
427
|
{
|
|
428
428
|
++ptr;
|
|
429
|
+
int peercoin_digits = 6;
|
|
429
430
|
if (ptr < end && IsDigit(val[ptr]))
|
|
430
431
|
{
|
|
431
432
|
while (ptr < end && IsDigit(val[ptr])) {
|
|
432
|
-
if (
|
|
433
|
-
|
|
433
|
+
if (peercoin_digits) {
|
|
434
|
+
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
|
|
435
|
+
return false; /* overflow */
|
|
436
|
+
++point_ofs;
|
|
437
|
+
--peercoin_digits;
|
|
438
|
+
}
|
|
434
439
|
++ptr;
|
|
435
|
-
++point_ofs;
|
|
436
440
|
}
|
|
437
441
|
} else return false; /* missing expected digit */
|
|
438
442
|
}
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
// Application startup time (used for uptime calculation)
|
|
91
91
|
const int64_t nStartupTime = GetTime();
|
|
92
92
|
|
|
93
|
-
const char * const BITCOIN_CONF_FILENAME = "
|
|
93
|
+
const char * const BITCOIN_CONF_FILENAME = "peercoin.conf";
|
|
94
94
|
const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
|
|
95
95
|
|
|
96
96
|
ArgsManager gArgs;
|
|
@@ -775,7 +775,7 @@ static std::string FormatException(const std::exception* pex, const char* pszThr
|
|
|
775
775
|
char pszModule[MAX_PATH] = "";
|
|
776
776
|
GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule));
|
|
777
777
|
#else
|
|
778
|
-
const char* pszModule = "
|
|
778
|
+
const char* pszModule = "peercoin";
|
|
779
779
|
#endif
|
|
780
780
|
if (pex)
|
|
781
781
|
return strprintf(
|
|
@@ -794,12 +794,13 @@ void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
|
|
|
794
794
|
|
|
795
795
|
fs::path GetDefaultDataDir()
|
|
796
796
|
{
|
|
797
|
-
// Windows: C:\
|
|
798
|
-
//
|
|
799
|
-
//
|
|
797
|
+
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Peercoin
|
|
798
|
+
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Peercoin
|
|
799
|
+
// Mac: ~/Library/Application Support/Peercoin
|
|
800
|
+
// Unix: ~/.peercoin
|
|
800
801
|
#ifdef WIN32
|
|
801
802
|
// Windows
|
|
802
|
-
return GetSpecialFolderPath(CSIDL_APPDATA) / "
|
|
803
|
+
return GetSpecialFolderPath(CSIDL_APPDATA) / "Peercoin";
|
|
803
804
|
#else
|
|
804
805
|
fs::path pathRet;
|
|
805
806
|
char* pszHome = getenv("HOME");
|
|
@@ -808,11 +809,11 @@ fs::path GetDefaultDataDir()
|
|
|
808
809
|
else
|
|
809
810
|
pathRet = fs::path(pszHome);
|
|
810
811
|
#ifdef MAC_OSX
|
|
811
|
-
//
|
|
812
|
-
return pathRet / "Library/Application Support/
|
|
812
|
+
// Mac
|
|
813
|
+
return pathRet / "Library/Application Support/Peercoin";
|
|
813
814
|
#else
|
|
814
|
-
// Unix
|
|
815
|
-
return pathRet / ".
|
|
815
|
+
// Unix
|
|
816
|
+
return pathRet / ".peercoin";
|
|
816
817
|
#endif
|
|
817
818
|
#endif
|
|
818
819
|
}
|
|
@@ -16,18 +16,18 @@
|
|
|
16
16
|
#include <consensus/tx_verify.h>
|
|
17
17
|
#include <consensus/validation.h>
|
|
18
18
|
#include <cuckoocache.h>
|
|
19
|
-
#include <deploymentstatus.h>
|
|
20
19
|
#include <flatfile.h>
|
|
21
20
|
#include <hash.h>
|
|
22
21
|
#include <index/blockfilterindex.h>
|
|
22
|
+
#include <index/txindex.h>
|
|
23
23
|
#include <logging.h>
|
|
24
24
|
#include <logging/timer.h>
|
|
25
|
+
#include <net.h>
|
|
25
26
|
#include <node/blockstorage.h>
|
|
26
27
|
#include <node/coinstats.h>
|
|
27
28
|
#include <node/ui_interface.h>
|
|
28
29
|
#include <node/utxo_snapshot.h>
|
|
29
30
|
#include <policy/policy.h>
|
|
30
|
-
#include <policy/rbf.h>
|
|
31
31
|
#include <policy/settings.h>
|
|
32
32
|
#include <pow.h>
|
|
33
33
|
#include <primitives/block.h>
|
|
@@ -47,7 +47,6 @@
|
|
|
47
47
|
#include <util/check.h> // For NDEBUG compile time check
|
|
48
48
|
#include <util/hasher.h>
|
|
49
49
|
#include <util/moneystr.h>
|
|
50
|
-
#include <util/rbf.h>
|
|
51
50
|
#include <util/strencodings.h>
|
|
52
51
|
#include <util/system.h>
|
|
53
52
|
#include <util/trace.h>
|
|
@@ -58,6 +57,10 @@
|
|
|
58
57
|
#include <algorithm>
|
|
59
58
|
#include <numeric>
|
|
60
59
|
#include <optional>
|
|
60
|
+
#include <kernel.h>
|
|
61
|
+
#include <bignum.h>
|
|
62
|
+
#include <wallet/wallet.h>
|
|
63
|
+
|
|
61
64
|
#include <string>
|
|
62
65
|
|
|
63
66
|
#include <boost/algorithm/string/replace.hpp>
|
|
@@ -74,12 +77,8 @@ using node::ReadBlockFromDisk;
|
|
|
74
77
|
using node::SnapshotMetadata;
|
|
75
78
|
using node::UNDOFILE_CHUNK_SIZE;
|
|
76
79
|
using node::UndoReadFromDisk;
|
|
77
|
-
using node::UnlinkPrunedFiles;
|
|
78
|
-
using node::fHavePruned;
|
|
79
80
|
using node::fImporting;
|
|
80
|
-
using node::fPruneMode;
|
|
81
81
|
using node::fReindex;
|
|
82
|
-
using node::nPruneTarget;
|
|
83
82
|
|
|
84
83
|
#define MICRO 0.000001
|
|
85
84
|
#define MILLI 0.001
|
|
@@ -109,8 +108,8 @@ const std::vector<std::string> CHECKLEVEL_DOC {
|
|
|
109
108
|
|
|
110
109
|
bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
|
|
111
110
|
// First sort by most total work, ...
|
|
112
|
-
if (pa->
|
|
113
|
-
if (pa->
|
|
111
|
+
if (pa->nChainTrust > pb->nChainTrust) return false;
|
|
112
|
+
if (pa->nChainTrust < pb->nChainTrust) return true;
|
|
114
113
|
|
|
115
114
|
// ... then by earliest time received, ...
|
|
116
115
|
if (pa->nSequenceId < pb->nSequenceId) return false;
|
|
@@ -125,6 +124,7 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIn
|
|
|
125
124
|
return false;
|
|
126
125
|
}
|
|
127
126
|
|
|
127
|
+
uint256 vStakeSeen[1024];
|
|
128
128
|
/**
|
|
129
129
|
* Mutex to guard access to validation specific variables, such as reading
|
|
130
130
|
* or changing the chainstate.
|
|
@@ -150,8 +150,7 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
|
|
|
150
150
|
uint256 hashAssumeValid;
|
|
151
151
|
arith_uint256 nMinimumChainWork;
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
CTxMemPool mempool;
|
|
155
154
|
CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
|
|
156
155
|
{
|
|
157
156
|
AssertLockHeld(cs_main);
|
|
@@ -301,18 +300,6 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, siz
|
|
|
301
300
|
coins_cache.Uncache(removed);
|
|
302
301
|
}
|
|
303
302
|
|
|
304
|
-
static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
|
305
|
-
{
|
|
306
|
-
AssertLockHeld(cs_main);
|
|
307
|
-
if (active_chainstate.IsInitialBlockDownload())
|
|
308
|
-
return false;
|
|
309
|
-
if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
|
|
310
|
-
return false;
|
|
311
|
-
if (active_chainstate.m_chain.Height() < pindexBestHeader->nHeight - 1)
|
|
312
|
-
return false;
|
|
313
|
-
return true;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
303
|
void CChainState::MaybeUpdateMempoolForReorg(
|
|
317
304
|
DisconnectedBlockTransactions& disconnectpool,
|
|
318
305
|
bool fAddToMempool)
|
|
@@ -331,7 +318,7 @@ void CChainState::MaybeUpdateMempoolForReorg(
|
|
|
331
318
|
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin();
|
|
332
319
|
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
|
|
333
320
|
// ignore validation errors in resurrected transactions
|
|
334
|
-
if (!fAddToMempool || (*it)->IsCoinBase() ||
|
|
321
|
+
if (!fAddToMempool || (*it)->IsCoinBase() || (*it)->IsCoinStake() ||
|
|
335
322
|
AcceptToMemoryPool(*this, *it, GetTime(),
|
|
336
323
|
/*bypass_limits=*/true, /*test_accept=*/false).m_result_type !=
|
|
337
324
|
MempoolAcceptResult::ResultType::VALID) {
|
|
@@ -423,7 +410,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
|
|
|
423
410
|
AssertLockHeld(cs_main);
|
|
424
411
|
AssertLockHeld(pool.cs);
|
|
425
412
|
|
|
426
|
-
assert(!tx.IsCoinBase());
|
|
413
|
+
assert(!tx.IsCoinBase() && !tx.IsCoinStake());
|
|
427
414
|
for (const CTxIn& txin : tx.vin) {
|
|
428
415
|
const Coin& coin = view.AccessCoin(txin.prevout);
|
|
429
416
|
|
|
@@ -598,9 +585,6 @@ private:
|
|
|
598
585
|
// only tests that are fast should be done here (to avoid CPU DoS).
|
|
599
586
|
bool PreChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
|
600
587
|
|
|
601
|
-
// Run checks for mempool replace-by-fee.
|
|
602
|
-
bool ReplacementChecks(Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
|
603
|
-
|
|
604
588
|
// Enforce package mempool ancestor/descendant limits (distinct from individual
|
|
605
589
|
// ancestor/descendant limits done in PreChecks).
|
|
606
590
|
bool PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
|
|
@@ -629,22 +613,6 @@ private:
|
|
|
629
613
|
std::map<const uint256, const MempoolAcceptResult>& results)
|
|
630
614
|
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
|
631
615
|
|
|
632
|
-
// Compare a package's feerate against minimum allowed.
|
|
633
|
-
bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_pool.cs)
|
|
634
|
-
{
|
|
635
|
-
AssertLockHeld(::cs_main);
|
|
636
|
-
AssertLockHeld(m_pool.cs);
|
|
637
|
-
CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size);
|
|
638
|
-
if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) {
|
|
639
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee));
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
if (package_fee < ::minRelayTxFee.GetFee(package_size)) {
|
|
643
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetFee(package_size)));
|
|
644
|
-
}
|
|
645
|
-
return true;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
616
|
private:
|
|
649
617
|
CTxMemPool& m_pool;
|
|
650
618
|
CCoinsViewCache m_view;
|
|
@@ -660,9 +628,6 @@ private:
|
|
|
660
628
|
// in-mempool conflicts; see below).
|
|
661
629
|
size_t m_limit_descendants;
|
|
662
630
|
size_t m_limit_descendant_size;
|
|
663
|
-
|
|
664
|
-
/** Whether the transaction(s) would replace any mempool transactions. If so, RBF rules apply. */
|
|
665
|
-
bool m_rbf{false};
|
|
666
631
|
};
|
|
667
632
|
|
|
668
633
|
bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
@@ -675,7 +640,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
675
640
|
|
|
676
641
|
// Copy/alias what we need out of args
|
|
677
642
|
const int64_t nAcceptTime = args.m_accept_time;
|
|
678
|
-
const bool bypass_limits = args.m_bypass_limits;
|
|
679
643
|
std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
|
|
680
644
|
|
|
681
645
|
// Alias what we need out of ws
|
|
@@ -685,9 +649,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
685
649
|
if (!CheckTransaction(tx, state)) {
|
|
686
650
|
return false; // state filled in by CheckTransaction
|
|
687
651
|
}
|
|
652
|
+
// Time (prevent mempool memory exhaustion attack)
|
|
653
|
+
// moved from CheckTransaction() to here, because it makes no sense to make GetAdjustedTime() a part of the consensus rules - user can set his clock to whatever he wishes.
|
|
654
|
+
if (tx.nTime > GetAdjustedTime() + (IsProtocolV09(GetAdjustedTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9))
|
|
655
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "timestamp-too-far");
|
|
688
656
|
|
|
689
657
|
// Coinbase is only valid in a block, not as a loose transaction
|
|
690
|
-
if (tx.IsCoinBase())
|
|
658
|
+
if (tx.IsCoinBase() || tx.IsCoinStake())
|
|
691
659
|
return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase");
|
|
692
660
|
|
|
693
661
|
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
|
@@ -722,25 +690,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
722
690
|
{
|
|
723
691
|
const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
|
|
724
692
|
if (ptxConflicting) {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
|
|
728
|
-
}
|
|
729
|
-
if (!ws.m_conflicts.count(ptxConflicting->GetHash()))
|
|
730
|
-
{
|
|
731
|
-
// Transactions that don't explicitly signal replaceability are
|
|
732
|
-
// *not* replaceable with the current logic, even if one of their
|
|
733
|
-
// unconfirmed ancestors signals replaceability. This diverges
|
|
734
|
-
// from BIP125's inherited signaling description (see CVE-2021-31876).
|
|
735
|
-
// Applications relying on first-seen mempool behavior should
|
|
736
|
-
// check all unconfirmed ancestors; otherwise an opt-in ancestor
|
|
737
|
-
// might be replaced, causing removal of this descendant.
|
|
738
|
-
if (!SignalsOptInRBF(*ptxConflicting)) {
|
|
739
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
ws.m_conflicts.insert(ptxConflicting->GetHash());
|
|
743
|
-
}
|
|
693
|
+
// Disable replacement feature for now
|
|
694
|
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
|
|
744
695
|
}
|
|
745
696
|
}
|
|
746
697
|
|
|
@@ -790,10 +741,12 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
790
741
|
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
|
|
791
742
|
|
|
792
743
|
// The mempool holds txs for the next block, so pass height+1 to CheckTxInputs
|
|
793
|
-
if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees)) {
|
|
744
|
+
if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees, Params().GetConsensus(), tx.nTime ? tx.nTime : GetAdjustedTime())) {
|
|
794
745
|
return false; // state filled in by CheckTxInputs
|
|
795
746
|
}
|
|
796
747
|
|
|
748
|
+
if (ws.m_base_fees < GetMinFee(tx, tx.nTime ? tx.nTime : GetAdjustedTime()))
|
|
749
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "fee is below minimum");
|
|
797
750
|
// Check for non-standard pay-to-script-hash in inputs
|
|
798
751
|
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
|
|
799
752
|
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
|
|
@@ -814,7 +767,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
814
767
|
bool fSpendsCoinbase = false;
|
|
815
768
|
for (const CTxIn &txin : tx.vin) {
|
|
816
769
|
const Coin &coin = m_view.AccessCoin(txin.prevout);
|
|
817
|
-
if (coin.IsCoinBase()) {
|
|
770
|
+
if (coin.IsCoinBase() || coin.IsCoinStake()) {
|
|
818
771
|
fSpendsCoinbase = true;
|
|
819
772
|
break;
|
|
820
773
|
}
|
|
@@ -828,46 +781,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
828
781
|
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
|
|
829
782
|
strprintf("%d", nSigOpsCost));
|
|
830
783
|
|
|
831
|
-
// No transactions are allowed below minRelayTxFee except from disconnected
|
|
832
|
-
// blocks
|
|
833
|
-
if (!bypass_limits && !CheckFeeRate(ws.m_vsize, ws.m_modified_fees, state)) return false;
|
|
834
|
-
|
|
835
|
-
ws.m_iters_conflicting = m_pool.GetIterSet(ws.m_conflicts);
|
|
836
|
-
// Calculate in-mempool ancestors, up to a limit.
|
|
837
|
-
if (ws.m_conflicts.size() == 1) {
|
|
838
|
-
// In general, when we receive an RBF transaction with mempool conflicts, we want to know whether we
|
|
839
|
-
// would meet the chain limits after the conflicts have been removed. However, there isn't a practical
|
|
840
|
-
// way to do this short of calculating the ancestor and descendant sets with an overlay cache of
|
|
841
|
-
// changed mempool entries. Due to both implementation and runtime complexity concerns, this isn't
|
|
842
|
-
// very realistic, thus we only ensure a limited set of transactions are RBF'able despite mempool
|
|
843
|
-
// conflicts here. Importantly, we need to ensure that some transactions which were accepted using
|
|
844
|
-
// the below carve-out are able to be RBF'ed, without impacting the security the carve-out provides
|
|
845
|
-
// for off-chain contract systems (see link in the comment below).
|
|
846
|
-
//
|
|
847
|
-
// Specifically, the subset of RBF transactions which we allow despite chain limits are those which
|
|
848
|
-
// conflict directly with exactly one other transaction (but may evict children of said transaction),
|
|
849
|
-
// and which are not adding any new mempool dependencies. Note that the "no new mempool dependencies"
|
|
850
|
-
// check is accomplished later, so we don't bother doing anything about it here, but if BIP 125 is
|
|
851
|
-
// amended, we may need to move that check to here instead of removing it wholesale.
|
|
852
|
-
//
|
|
853
|
-
// Such transactions are clearly not merging any existing packages, so we are only concerned with
|
|
854
|
-
// ensuring that (a) no package is growing past the package size (not count) limits and (b) we are
|
|
855
|
-
// not allowing something to effectively use the (below) carve-out spot when it shouldn't be allowed
|
|
856
|
-
// to.
|
|
857
|
-
//
|
|
858
|
-
// To check these we first check if we meet the RBF criteria, above, and increment the descendant
|
|
859
|
-
// limits by the direct conflict and its descendants (as these are recalculated in
|
|
860
|
-
// CalculateMempoolAncestors by assuming the new transaction being added is a new descendant, with no
|
|
861
|
-
// removals, of each parent's existing dependent set). The ancestor count limits are unmodified (as
|
|
862
|
-
// the ancestor limits should be the same for both our new transaction and any conflicts).
|
|
863
|
-
// We don't bother incrementing m_limit_descendants by the full removal count as that limit never comes
|
|
864
|
-
// into force here (as we're only adding a single transaction).
|
|
865
|
-
assert(ws.m_iters_conflicting.size() == 1);
|
|
866
|
-
CTxMemPool::txiter conflict = *ws.m_iters_conflicting.begin();
|
|
867
|
-
|
|
868
|
-
m_limit_descendants += 1;
|
|
869
|
-
m_limit_descendant_size += conflict->GetSizeWithDescendants();
|
|
870
|
-
}
|
|
871
784
|
|
|
872
785
|
std::string errString;
|
|
873
786
|
if (!m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, errString)) {
|
|
@@ -891,63 +804,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
891
804
|
}
|
|
892
805
|
}
|
|
893
806
|
|
|
894
|
-
// A transaction that spends outputs that would be replaced by it is invalid. Now
|
|
895
|
-
// that we have the set of all ancestors we can detect this
|
|
896
|
-
// pathological case by making sure ws.m_conflicts and ws.m_ancestors don't
|
|
897
|
-
// intersect.
|
|
898
|
-
if (const auto err_string{EntriesAndTxidsDisjoint(ws.m_ancestors, ws.m_conflicts, hash)}) {
|
|
899
|
-
// We classify this as a consensus error because a transaction depending on something it
|
|
900
|
-
// conflicts with would be inconsistent.
|
|
901
|
-
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spends-conflicting-tx", *err_string);
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
m_rbf = !ws.m_conflicts.empty();
|
|
905
|
-
return true;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
bool MemPoolAccept::ReplacementChecks(Workspace& ws)
|
|
909
|
-
{
|
|
910
|
-
AssertLockHeld(cs_main);
|
|
911
|
-
AssertLockHeld(m_pool.cs);
|
|
912
|
-
|
|
913
|
-
const CTransaction& tx = *ws.m_ptx;
|
|
914
|
-
const uint256& hash = ws.m_hash;
|
|
915
|
-
TxValidationState& state = ws.m_state;
|
|
916
|
-
|
|
917
|
-
CFeeRate newFeeRate(ws.m_modified_fees, ws.m_vsize);
|
|
918
|
-
// The replacement transaction must have a higher feerate than its direct conflicts.
|
|
919
|
-
// - The motivation for this check is to ensure that the replacement transaction is preferable for
|
|
920
|
-
// block-inclusion, compared to what would be removed from the mempool.
|
|
921
|
-
// - This logic predates ancestor feerate-based transaction selection, which is why it doesn't
|
|
922
|
-
// consider feerates of descendants.
|
|
923
|
-
// - Note: Ancestor feerate-based transaction selection has made this comparison insufficient to
|
|
924
|
-
// guarantee that this is incentive-compatible for miners, because it is possible for a
|
|
925
|
-
// descendant transaction of a direct conflict to pay a higher feerate than the transaction that
|
|
926
|
-
// might replace them, under these rules.
|
|
927
|
-
if (const auto err_string{PaysMoreThanConflicts(ws.m_iters_conflicting, newFeeRate, hash)}) {
|
|
928
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// Calculate all conflicting entries and enforce BIP125 Rule #5.
|
|
932
|
-
if (const auto err_string{GetEntriesForConflicts(tx, m_pool, ws.m_iters_conflicting, ws.m_all_conflicting)}) {
|
|
933
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
|
|
934
|
-
"too many potential replacements", *err_string);
|
|
935
|
-
}
|
|
936
|
-
// Enforce BIP125 Rule #2.
|
|
937
|
-
if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, ws.m_iters_conflicting)}) {
|
|
938
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
|
|
939
|
-
"replacement-adds-unconfirmed", *err_string);
|
|
940
|
-
}
|
|
941
|
-
// Check if it's economically rational to mine this transaction rather than the ones it
|
|
942
|
-
// replaces and pays for its own relay fees. Enforce BIP125 Rules #3 and #4.
|
|
943
|
-
for (CTxMemPool::txiter it : ws.m_all_conflicting) {
|
|
944
|
-
ws.m_conflicting_fees += it->GetModifiedFee();
|
|
945
|
-
ws.m_conflicting_size += it->GetTxSize();
|
|
946
|
-
}
|
|
947
|
-
if (const auto err_string{PaysForRBF(ws.m_conflicting_fees, ws.m_modified_fees, ws.m_vsize,
|
|
948
|
-
::incrementalRelayFee, hash)}) {
|
|
949
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
|
|
950
|
-
}
|
|
951
807
|
return true;
|
|
952
808
|
}
|
|
953
809
|
|
|
@@ -977,7 +833,16 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
|
|
|
977
833
|
const CTransaction& tx = *ws.m_ptx;
|
|
978
834
|
TxValidationState& state = ws.m_state;
|
|
979
835
|
|
|
980
|
-
|
|
836
|
+
unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
|
837
|
+
|
|
838
|
+
// peercoin: if transaction is after version 0.8 fork, verify SCRIPT_VERIFY_LOW_S
|
|
839
|
+
// ppcTODO move back to policy.h after 0.8 is active
|
|
840
|
+
//if (IsBTC16BIPsEnabled(tx.nTime))
|
|
841
|
+
// scriptVerifyFlags &= SCRIPT_VERIFY_LOW_S;
|
|
842
|
+
|
|
843
|
+
// peercoin allow taproot after fork
|
|
844
|
+
//if (IsProtocolV12(tx.nTime))
|
|
845
|
+
// scriptVerifyFlags &= SCRIPT_VERIFY_TAPROOT;
|
|
981
846
|
|
|
982
847
|
// Check input scripts and signatures.
|
|
983
848
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
|
@@ -1036,35 +901,14 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
|
|
|
1036
901
|
{
|
|
1037
902
|
AssertLockHeld(cs_main);
|
|
1038
903
|
AssertLockHeld(m_pool.cs);
|
|
1039
|
-
const CTransaction& tx = *ws.m_ptx;
|
|
1040
904
|
const uint256& hash = ws.m_hash;
|
|
1041
905
|
TxValidationState& state = ws.m_state;
|
|
1042
906
|
const bool bypass_limits = args.m_bypass_limits;
|
|
1043
907
|
|
|
1044
908
|
std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
|
|
1045
909
|
|
|
1046
|
-
// Remove conflicting transactions from the mempool
|
|
1047
|
-
for (CTxMemPool::txiter it : ws.m_all_conflicting)
|
|
1048
|
-
{
|
|
1049
|
-
LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s additional fees, %d delta bytes\n",
|
|
1050
|
-
it->GetTx().GetHash().ToString(),
|
|
1051
|
-
hash.ToString(),
|
|
1052
|
-
FormatMoney(ws.m_modified_fees - ws.m_conflicting_fees),
|
|
1053
|
-
(int)entry->GetTxSize() - (int)ws.m_conflicting_size);
|
|
1054
|
-
ws.m_replaced_transactions.push_back(it->GetSharedTx());
|
|
1055
|
-
}
|
|
1056
|
-
m_pool.RemoveStaged(ws.m_all_conflicting, false, MemPoolRemovalReason::REPLACED);
|
|
1057
|
-
|
|
1058
|
-
// This transaction should only count for fee estimation if:
|
|
1059
|
-
// - it's not being re-added during a reorg which bypasses typical mempool fee limits
|
|
1060
|
-
// - the node is not behind
|
|
1061
|
-
// - the transaction is not dependent on any other transactions in the mempool
|
|
1062
|
-
// - it's not part of a package. Since package relay is not currently supported, this
|
|
1063
|
-
// transaction has not necessarily been accepted to miners' mempools.
|
|
1064
|
-
bool validForFeeEstimation = !bypass_limits && !args.m_package_submission && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
|
|
1065
|
-
|
|
1066
910
|
// Store transaction in memory
|
|
1067
|
-
m_pool.addUnchecked(*entry, ws.m_ancestors
|
|
911
|
+
m_pool.addUnchecked(*entry, ws.m_ancestors);
|
|
1068
912
|
|
|
1069
913
|
// trim mempool and check if tx was trimmed
|
|
1070
914
|
// If we are validating a package, don't trim here because we could evict a previous transaction
|
|
@@ -1162,8 +1006,6 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
|
|
|
1162
1006
|
|
|
1163
1007
|
if (!PreChecks(args, ws)) return MempoolAcceptResult::Failure(ws.m_state);
|
|
1164
1008
|
|
|
1165
|
-
if (m_rbf && !ReplacementChecks(ws)) return MempoolAcceptResult::Failure(ws.m_state);
|
|
1166
|
-
|
|
1167
1009
|
// Perform the inexpensive checks first and avoid hashing and signature verification unless
|
|
1168
1010
|
// those checks pass, to mitigate CPU exhaustion denial-of-service attacks.
|
|
1169
1011
|
if (!PolicyScriptChecks(args, ws)) return MempoolAcceptResult::Failure(ws.m_state);
|
|
@@ -1414,16 +1256,67 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
|
|
|
1414
1256
|
return result;
|
|
1415
1257
|
}
|
|
1416
1258
|
|
|
1417
|
-
|
|
1259
|
+
int64_t GetProofOfWorkReward(unsigned int nBits, uint32_t nTime)
|
|
1418
1260
|
{
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
//
|
|
1426
|
-
|
|
1261
|
+
CBigNum bnSubsidyLimit = MAX_MINT_PROOF_OF_WORK;
|
|
1262
|
+
CBigNum bnTarget;
|
|
1263
|
+
bnTarget.SetCompact(nBits);
|
|
1264
|
+
CBigNum bnTargetLimit(Params().GetConsensus().powLimit);
|
|
1265
|
+
bnTargetLimit.SetCompact(bnTargetLimit.GetCompact());
|
|
1266
|
+
|
|
1267
|
+
// peercoin: subsidy is cut in half every 16x multiply of difficulty
|
|
1268
|
+
// A reasonably continuous curve is used to avoid shock to market
|
|
1269
|
+
// (nSubsidyLimit / nSubsidy) ** 4 == bnProofOfWorkLimit / bnTarget
|
|
1270
|
+
CBigNum bnLowerBound = CENT;
|
|
1271
|
+
CBigNum bnUpperBound = bnSubsidyLimit;
|
|
1272
|
+
while (bnLowerBound + CENT <= bnUpperBound)
|
|
1273
|
+
{
|
|
1274
|
+
CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2;
|
|
1275
|
+
if (gArgs.GetBoolArg("-printcreation", false))
|
|
1276
|
+
LogPrintf("%s: lower=%lld upper=%lld mid=%lld\n", __func__, bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64());
|
|
1277
|
+
if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget)
|
|
1278
|
+
bnUpperBound = bnMidValue;
|
|
1279
|
+
else
|
|
1280
|
+
bnLowerBound = bnMidValue;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
int64_t nSubsidy = bnUpperBound.getuint64();
|
|
1284
|
+
nSubsidy = (nSubsidy / CENT) * CENT;
|
|
1285
|
+
|
|
1286
|
+
nSubsidy = std::min(nSubsidy, IsProtocolV10(nTime) ? MAX_MINT_PROOF_OF_WORK_V10 : MAX_MINT_PROOF_OF_WORK);
|
|
1287
|
+
|
|
1288
|
+
if (gArgs.GetBoolArg("-printcreation", false))
|
|
1289
|
+
LogPrintf("%s: create=%s nBits=0x%08x nSubsidy=%lld\n", __func__, FormatMoney(nSubsidy), nBits, nSubsidy);
|
|
1290
|
+
|
|
1291
|
+
return nSubsidy;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// peercoin: miner's coin stake is rewarded based on coin age spent (coin-days)
|
|
1295
|
+
int64_t GetProofOfStakeReward(int64_t nCoinAge, uint32_t nTime, uint64_t nMoneySupply)
|
|
1296
|
+
{
|
|
1297
|
+
static int64_t nRewardCoinYear = CENT; // creation amount per coin-year
|
|
1298
|
+
int64_t nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear;
|
|
1299
|
+
|
|
1300
|
+
if (IsProtocolV09(nTime)) {
|
|
1301
|
+
// rfc18
|
|
1302
|
+
// YearlyBlocks = ((365 * 33 + 8) / 33) * 1440 / 10
|
|
1303
|
+
// some efforts not to lose precision
|
|
1304
|
+
CBigNum bnInflationAdjustment = nMoneySupply;
|
|
1305
|
+
bnInflationAdjustment *= 25 * 33;
|
|
1306
|
+
bnInflationAdjustment /= 10000 * 144;
|
|
1307
|
+
bnInflationAdjustment /= (365 * 33 + 8);
|
|
1308
|
+
|
|
1309
|
+
uint64_t nInflationAdjustment = bnInflationAdjustment.getuint64();
|
|
1310
|
+
uint64_t nSubsidyNew = (nSubsidy * 3) + nInflationAdjustment;
|
|
1311
|
+
|
|
1312
|
+
if (gArgs.GetBoolArg("-printcreation", false))
|
|
1313
|
+
LogPrintf("%s: money supply %ld, inflation adjustment %f, old subsidy %ld, new subsidy %ld\n", __func__, nMoneySupply, nInflationAdjustment/1000000.0, nSubsidy, nSubsidyNew);
|
|
1314
|
+
|
|
1315
|
+
nSubsidy = nSubsidyNew;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
if (gArgs.GetBoolArg("-printcreation", false))
|
|
1319
|
+
LogPrintf("%s: create=%s nCoinAge=%lld\n", __func__, FormatMoney(nSubsidy), nCoinAge);
|
|
1427
1320
|
return nSubsidy;
|
|
1428
1321
|
}
|
|
1429
1322
|
|
|
@@ -1492,7 +1385,7 @@ bool CChainState::IsInitialBlockDownload() const
|
|
|
1492
1385
|
return true;
|
|
1493
1386
|
if (m_chain.Tip() == nullptr)
|
|
1494
1387
|
return true;
|
|
1495
|
-
if (m_chain.Tip()->
|
|
1388
|
+
if (m_chain.Tip()->nChainTrust < nMinimumChainWork)
|
|
1496
1389
|
return true;
|
|
1497
1390
|
if (m_chain.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
|
|
1498
1391
|
return true;
|
|
@@ -1501,9 +1394,11 @@ bool CChainState::IsInitialBlockDownload() const
|
|
|
1501
1394
|
return false;
|
|
1502
1395
|
}
|
|
1503
1396
|
|
|
1504
|
-
|
|
1397
|
+
|
|
1398
|
+
void AlertNotify(const std::string& strMessage, bool fUpdateUI)
|
|
1505
1399
|
{
|
|
1506
|
-
|
|
1400
|
+
if (fUpdateUI)
|
|
1401
|
+
uiInterface.NotifyAlertChanged(uint256(), CT_UPDATED); // peercoin: we are using arguments that will have no effects in updateAlert()
|
|
1507
1402
|
#if HAVE_SYSTEM
|
|
1508
1403
|
std::string strCmd = gArgs.GetArg("-alertnotify", "");
|
|
1509
1404
|
if (strCmd.empty()) return;
|
|
@@ -1531,7 +1426,7 @@ void CChainState::CheckForkWarningConditions()
|
|
|
1531
1426
|
return;
|
|
1532
1427
|
}
|
|
1533
1428
|
|
|
1534
|
-
if (m_chainman.m_best_invalid && m_chainman.m_best_invalid->
|
|
1429
|
+
if (m_chainman.m_best_invalid && m_chainman.m_best_invalid->nChainTrust > m_chain.Tip()->nChainTrust + (GetBlockTrust(*m_chain.Tip()) * 6)) {
|
|
1535
1430
|
LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__);
|
|
1536
1431
|
SetfLargeWorkInvalidChainFound(true);
|
|
1537
1432
|
} else {
|
|
@@ -1543,21 +1438,25 @@ void CChainState::CheckForkWarningConditions()
|
|
|
1543
1438
|
void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
|
|
1544
1439
|
{
|
|
1545
1440
|
AssertLockHeld(cs_main);
|
|
1546
|
-
if (!m_chainman.m_best_invalid || pindexNew->
|
|
1441
|
+
if (!m_chainman.m_best_invalid || pindexNew->nChainTrust > m_chainman.m_best_invalid->nChainTrust) {
|
|
1547
1442
|
m_chainman.m_best_invalid = pindexNew;
|
|
1548
1443
|
}
|
|
1549
1444
|
if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
|
|
1550
1445
|
pindexBestHeader = m_chain.Tip();
|
|
1551
1446
|
}
|
|
1552
1447
|
|
|
1553
|
-
LogPrintf("%s: invalid block=%s height=%d
|
|
1448
|
+
LogPrintf("%s: invalid block=%s height=%d log2_trust=%.8g moneysupply=%s date=%s moneysupply=%s\n", __func__,
|
|
1554
1449
|
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
|
|
1555
|
-
log(pindexNew->
|
|
1450
|
+
log(pindexNew->nChainTrust.getdouble())/log(2.0),
|
|
1451
|
+
FormatMoney(m_chain.Tip()->nMoneySupply),
|
|
1452
|
+
FormatISO8601DateTime(pindexNew->GetBlockTime()),
|
|
1453
|
+
FormatMoney(pindexNew->nMoneySupply));
|
|
1556
1454
|
CBlockIndex *tip = m_chain.Tip();
|
|
1557
1455
|
assert (tip);
|
|
1558
|
-
LogPrintf("%s: current best=%s height=%d
|
|
1559
|
-
tip->GetBlockHash().ToString(), m_chain.Height(), log(tip->
|
|
1560
|
-
|
|
1456
|
+
LogPrintf("%s: current best=%s height=%d log2_trust=%.8g moneysupply=%s date=%s moneysupply=%s\n", __func__,
|
|
1457
|
+
tip->GetBlockHash().ToString(), m_chain.Height(), log(tip->nChainTrust.getdouble())/log(2.0),
|
|
1458
|
+
FormatMoney(tip->nMoneySupply),
|
|
1459
|
+
FormatISO8601DateTime(tip->GetBlockTime()), FormatMoney(pindexNew->nMoneySupply));
|
|
1561
1460
|
CheckForkWarningConditions();
|
|
1562
1461
|
}
|
|
1563
1462
|
|
|
@@ -1575,7 +1474,7 @@ void CChainState::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationSt
|
|
|
1575
1474
|
}
|
|
1576
1475
|
}
|
|
1577
1476
|
|
|
1578
|
-
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight)
|
|
1477
|
+
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight, bool skipZeroValue)
|
|
1579
1478
|
{
|
|
1580
1479
|
// mark inputs spent
|
|
1581
1480
|
if (!tx.IsCoinBase()) {
|
|
@@ -1587,7 +1486,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
|
|
|
1587
1486
|
}
|
|
1588
1487
|
}
|
|
1589
1488
|
// add outputs
|
|
1590
|
-
AddCoins(inputs, tx, nHeight);
|
|
1489
|
+
AddCoins(inputs, tx, nHeight, false, skipZeroValue);
|
|
1591
1490
|
}
|
|
1592
1491
|
|
|
1593
1492
|
bool CScriptCheck::operator()() {
|
|
@@ -1749,6 +1648,8 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
|
|
|
1749
1648
|
if (!alternate.IsSpent()) {
|
|
1750
1649
|
undo.nHeight = alternate.nHeight;
|
|
1751
1650
|
undo.fCoinBase = alternate.fCoinBase;
|
|
1651
|
+
undo.fCoinStake = alternate.fCoinStake; // peercoin
|
|
1652
|
+
undo.nTime = alternate.nTime; // peercoin
|
|
1752
1653
|
} else {
|
|
1753
1654
|
return DISCONNECT_FAILED; // adding output for transaction without known metadata
|
|
1754
1655
|
}
|
|
@@ -1758,7 +1659,7 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
|
|
|
1758
1659
|
// already checked whether an unspent coin exists above using HaveCoin, so
|
|
1759
1660
|
// we don't need to guess. When fClean is false, an unspent coin already
|
|
1760
1661
|
// existed and it is an overwrite.
|
|
1761
|
-
view.AddCoin(out, std::move(undo), !fClean);
|
|
1662
|
+
view.AddCoin(out, std::move(undo), !fClean, false);
|
|
1762
1663
|
|
|
1763
1664
|
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
|
1764
1665
|
}
|
|
@@ -1786,15 +1687,18 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
|
|
|
1786
1687
|
const CTransaction &tx = *(block.vtx[i]);
|
|
1787
1688
|
uint256 hash = tx.GetHash();
|
|
1788
1689
|
bool is_coinbase = tx.IsCoinBase();
|
|
1690
|
+
bool is_coinstake = tx.IsCoinStake();
|
|
1789
1691
|
|
|
1790
1692
|
// Check that all outputs are available and match the outputs in the block itself
|
|
1791
1693
|
// exactly.
|
|
1792
1694
|
for (size_t o = 0; o < tx.vout.size(); o++) {
|
|
1793
1695
|
if (!tx.vout[o].scriptPubKey.IsUnspendable()) {
|
|
1696
|
+
if (IsProtocolV12(pindex) && !tx.vout[o].nValue)
|
|
1697
|
+
continue;
|
|
1794
1698
|
COutPoint out(hash, o);
|
|
1795
1699
|
Coin coin;
|
|
1796
1700
|
bool is_spent = view.SpendCoin(out, &coin);
|
|
1797
|
-
if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) {
|
|
1701
|
+
if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase || is_coinstake != coin.fCoinStake) {
|
|
1798
1702
|
fClean = false; // transaction output mismatch
|
|
1799
1703
|
}
|
|
1800
1704
|
}
|
|
@@ -1836,33 +1740,6 @@ void StopScriptCheckWorkerThreads()
|
|
|
1836
1740
|
scriptcheckqueue.StopWorkerThreads();
|
|
1837
1741
|
}
|
|
1838
1742
|
|
|
1839
|
-
/**
|
|
1840
|
-
* Threshold condition checker that triggers when unknown versionbits are seen on the network.
|
|
1841
|
-
*/
|
|
1842
|
-
class WarningBitsConditionChecker : public AbstractThresholdConditionChecker
|
|
1843
|
-
{
|
|
1844
|
-
private:
|
|
1845
|
-
int bit;
|
|
1846
|
-
|
|
1847
|
-
public:
|
|
1848
|
-
explicit WarningBitsConditionChecker(int bitIn) : bit(bitIn) {}
|
|
1849
|
-
|
|
1850
|
-
int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
|
|
1851
|
-
int64_t EndTime(const Consensus::Params& params) const override { return std::numeric_limits<int64_t>::max(); }
|
|
1852
|
-
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
|
|
1853
|
-
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
|
|
1854
|
-
|
|
1855
|
-
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
|
|
1856
|
-
{
|
|
1857
|
-
return pindex->nHeight >= params.MinBIP9WarningHeight &&
|
|
1858
|
-
((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) &&
|
|
1859
|
-
((pindex->nVersion >> bit) & 1) != 0 &&
|
|
1860
|
-
((g_versionbitscache.ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0;
|
|
1861
|
-
}
|
|
1862
|
-
};
|
|
1863
|
-
|
|
1864
|
-
static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_main);
|
|
1865
|
-
|
|
1866
1743
|
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams)
|
|
1867
1744
|
{
|
|
1868
1745
|
unsigned int flags = SCRIPT_VERIFY_NONE;
|
|
@@ -1881,30 +1758,26 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
|
|
|
1881
1758
|
}
|
|
1882
1759
|
|
|
1883
1760
|
// Enforce the DERSIG (BIP66) rule
|
|
1884
|
-
if (
|
|
1761
|
+
if (pindex->pprev && IsBTC16BIPsEnabled(pindex->pprev->nTime)) {
|
|
1885
1762
|
flags |= SCRIPT_VERIFY_DERSIG;
|
|
1886
1763
|
}
|
|
1887
1764
|
|
|
1888
1765
|
// Enforce CHECKLOCKTIMEVERIFY (BIP65)
|
|
1889
|
-
if (
|
|
1766
|
+
if (IsProtocolV06(pindex->pprev)) {
|
|
1890
1767
|
flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
|
|
1891
1768
|
}
|
|
1892
1769
|
|
|
1893
|
-
// Enforce
|
|
1894
|
-
|
|
1895
|
-
|
|
1770
|
+
// Enforce BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY)
|
|
1771
|
+
// Enforce BIP147 NULLDUMMY (activated simultaneously with segwit)
|
|
1772
|
+
if (pindex->pprev && IsBTC16BIPsEnabled(pindex->pprev->nTime)) {
|
|
1773
|
+
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY | SCRIPT_VERIFY_NULLDUMMY;
|
|
1896
1774
|
}
|
|
1897
1775
|
|
|
1898
1776
|
// Enforce Taproot (BIP340-BIP342)
|
|
1899
|
-
if (
|
|
1777
|
+
if (pindex->pprev && IsProtocolV12(pindex->pprev)) {
|
|
1900
1778
|
flags |= SCRIPT_VERIFY_TAPROOT;
|
|
1901
1779
|
}
|
|
1902
1780
|
|
|
1903
|
-
// Enforce BIP147 NULLDUMMY (activated simultaneously with segwit)
|
|
1904
|
-
if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_SEGWIT)) {
|
|
1905
|
-
flags |= SCRIPT_VERIFY_NULLDUMMY;
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
1781
|
return flags;
|
|
1909
1782
|
}
|
|
1910
1783
|
|
|
@@ -1918,6 +1791,80 @@ static int64_t nTimeIndex = 0;
|
|
|
1918
1791
|
static int64_t nTimeTotal = 0;
|
|
1919
1792
|
static int64_t nBlocksTotal = 0;
|
|
1920
1793
|
|
|
1794
|
+
// These checks can only be done when all previous block have been added.
|
|
1795
|
+
bool PeercoinContextualBlockChecks(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex, bool fJustCheck, CChainState& chainstate)
|
|
1796
|
+
{
|
|
1797
|
+
uint256 hashProofOfStake = uint256();
|
|
1798
|
+
// peercoin: verify hash target and signature of coinstake tx
|
|
1799
|
+
if (block.IsProofOfStake() && !CheckProofOfStake(state, pindex->pprev, block.vtx[1], block.nBits, hashProofOfStake, block.vtx[1]->nTime ? block.vtx[1]->nTime : block.nTime, chainstate)) {
|
|
1800
|
+
LogPrintf("WARNING: %s: check proof-of-stake failed for block %s\n", __func__, block.GetHash().ToString());
|
|
1801
|
+
return false; // do not error here as we expect this during initial block download
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// peercoin: check for duplicity of stake
|
|
1805
|
+
if (block.IsProofOfStake()) {
|
|
1806
|
+
std::pair<COutPoint, unsigned int> proofOfStake = block.GetProofOfStake();
|
|
1807
|
+
if (pindex->IsProofOfStake() && proofOfStake.first == pindex->prevoutStake) {
|
|
1808
|
+
LogPrintf("WARNING: %s: duplicate proof-of-stake in block %s, invalidating tip\n", __func__, block.GetHash().ToString());
|
|
1809
|
+
chainstate.InvalidateBlock(state, pindex);
|
|
1810
|
+
return error("ConnectBlock() : Duplicate coinstake found");
|
|
1811
|
+
} else if (setStakeSeen.count(proofOfStake)) {
|
|
1812
|
+
LogPrintf("WARNING: %s: duplicate proof-of-stake in block %s\n", __func__, block.GetHash().ToString());
|
|
1813
|
+
return error("ConnectBlock() : Duplicate coinstake found");
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
// peercoin: compute stake entropy bit for stake modifier
|
|
1818
|
+
unsigned int nEntropyBit = GetStakeEntropyBit(block);
|
|
1819
|
+
|
|
1820
|
+
// peercoin: compute stake modifier
|
|
1821
|
+
uint64_t nStakeModifier = 0;
|
|
1822
|
+
bool fGeneratedStakeModifier = false;
|
|
1823
|
+
if (!ComputeNextStakeModifier(pindex, nStakeModifier, fGeneratedStakeModifier, chainstate))
|
|
1824
|
+
return error("ConnectBlock() : ComputeNextStakeModifier() failed");
|
|
1825
|
+
|
|
1826
|
+
// compute nStakeModifierChecksum begin
|
|
1827
|
+
unsigned int nFlagsBackup = pindex->nFlags;
|
|
1828
|
+
uint64_t nStakeModifierBackup = pindex->nStakeModifier;
|
|
1829
|
+
uint256 hashProofOfStakeBackup = pindex->hashProofOfStake;
|
|
1830
|
+
|
|
1831
|
+
// set necessary pindex fields
|
|
1832
|
+
if (!pindex->SetStakeEntropyBit(nEntropyBit))
|
|
1833
|
+
return error("ConnectBlock() : SetStakeEntropyBit() failed");
|
|
1834
|
+
pindex->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
|
|
1835
|
+
pindex->hashProofOfStake = hashProofOfStake;
|
|
1836
|
+
|
|
1837
|
+
unsigned int nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
|
|
1838
|
+
|
|
1839
|
+
// undo pindex fields
|
|
1840
|
+
pindex->nFlags = nFlagsBackup;
|
|
1841
|
+
pindex->nStakeModifier = nStakeModifierBackup;
|
|
1842
|
+
pindex->hashProofOfStake = hashProofOfStakeBackup;
|
|
1843
|
+
// compute nStakeModifierChecksum end
|
|
1844
|
+
|
|
1845
|
+
if (!CheckStakeModifierCheckpoints(pindex->nHeight, nStakeModifierChecksum))
|
|
1846
|
+
return error("ConnectBlock() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016llx", pindex->nHeight, nStakeModifier);
|
|
1847
|
+
|
|
1848
|
+
if (fJustCheck)
|
|
1849
|
+
return true;
|
|
1850
|
+
|
|
1851
|
+
// write everything to index
|
|
1852
|
+
if (block.IsProofOfStake())
|
|
1853
|
+
{
|
|
1854
|
+
pindex->prevoutStake = block.vtx[1]->vin[0].prevout;
|
|
1855
|
+
pindex->nStakeTime = block.vtx[1]->nTime;
|
|
1856
|
+
pindex->hashProofOfStake = hashProofOfStake;
|
|
1857
|
+
setStakeSeen.insert(std::make_pair(pindex->prevoutStake, pindex->nTime));
|
|
1858
|
+
}
|
|
1859
|
+
if (!pindex->SetStakeEntropyBit(nEntropyBit))
|
|
1860
|
+
return error("ConnectBlock() : SetStakeEntropyBit() failed");
|
|
1861
|
+
pindex->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
|
|
1862
|
+
pindex->nStakeModifierChecksum = nStakeModifierChecksum;
|
|
1863
|
+
chainstate.m_blockman.m_dirty_blockindex.insert(pindex); // queue a write to disk
|
|
1864
|
+
|
|
1865
|
+
return true;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1921
1868
|
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
|
|
1922
1869
|
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
|
|
1923
1870
|
* can fail if those validity checks fail (among other reasons). */
|
|
@@ -1932,6 +1879,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
1932
1879
|
|
|
1933
1880
|
int64_t nTimeStart = GetTimeMicros();
|
|
1934
1881
|
|
|
1882
|
+
if (pindex->nStakeModifier == 0 && pindex->nStakeModifierChecksum == 0 && !PeercoinContextualBlockChecks(block, state, pindex, fJustCheck, m_chainman.ActiveChainstate()))
|
|
1883
|
+
return error("%s: failed PoS check %s", __func__, state.ToString());
|
|
1884
|
+
|
|
1935
1885
|
// Check it again in case a previous version let a bad block in
|
|
1936
1886
|
// NOTE: We don't currently (re-)invoke ContextualCheckBlock() or
|
|
1937
1887
|
// ContextualCheckBlockHeader() here. This means that if we add a new
|
|
@@ -1980,7 +1930,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
1980
1930
|
if (it != m_blockman.m_block_index.end()) {
|
|
1981
1931
|
if (it->second->GetAncestor(pindex->nHeight) == pindex &&
|
|
1982
1932
|
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
|
|
1983
|
-
pindexBestHeader->
|
|
1933
|
+
pindexBestHeader->nChainTrust >= nMinimumChainWork) {
|
|
1984
1934
|
// This block is a member of the assumed verified chain and an ancestor of the best header.
|
|
1985
1935
|
// Script verification is skipped when connecting blocks under the
|
|
1986
1936
|
// assumevalid block. Assuming the assumevalid block is valid this
|
|
@@ -2013,8 +1963,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2013
1963
|
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
|
|
2014
1964
|
// two in the chain that violate it. This prevents exploiting the issue against nodes during their
|
|
2015
1965
|
// initial block download.
|
|
2016
|
-
bool fEnforceBIP30 = !
|
|
2017
|
-
(pindex->nHeight==91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
|
|
1966
|
+
bool fEnforceBIP30 = (!pindex->phashBlock); // Enforce on CreateNewBlock invocations which don't have a hash.
|
|
2018
1967
|
|
|
2019
1968
|
// Once BIP34 activated it was not possible to create new duplicate coinbases and thus other than starting
|
|
2020
1969
|
// with the 2 existing duplicate coinbase pairs, not possible to create overwriting txs. But by the
|
|
@@ -2090,9 +2039,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2090
2039
|
}
|
|
2091
2040
|
}
|
|
2092
2041
|
|
|
2093
|
-
// Enforce BIP68 (sequence locks)
|
|
2042
|
+
// Enforce BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY)
|
|
2094
2043
|
int nLockTimeFlags = 0;
|
|
2095
|
-
if (
|
|
2044
|
+
if (pindex->pprev && IsBTC16BIPsEnabled(pindex->pprev->nTime)) {
|
|
2096
2045
|
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE;
|
|
2097
2046
|
}
|
|
2098
2047
|
|
|
@@ -2114,6 +2063,8 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2114
2063
|
|
|
2115
2064
|
std::vector<int> prevheights;
|
|
2116
2065
|
CAmount nFees = 0;
|
|
2066
|
+
int64_t nValueIn = 0;
|
|
2067
|
+
int64_t nValueOut = 0;
|
|
2117
2068
|
int nInputs = 0;
|
|
2118
2069
|
int64_t nSigOpsCost = 0;
|
|
2119
2070
|
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
|
@@ -2123,17 +2074,23 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2123
2074
|
|
|
2124
2075
|
nInputs += tx.vin.size();
|
|
2125
2076
|
|
|
2126
|
-
if (
|
|
2077
|
+
if (tx.IsCoinBase())
|
|
2078
|
+
nValueOut += tx.GetValueOut();
|
|
2079
|
+
else
|
|
2127
2080
|
{
|
|
2128
2081
|
CAmount txfee = 0;
|
|
2129
2082
|
TxValidationState tx_state;
|
|
2130
|
-
if (!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, txfee)) {
|
|
2083
|
+
if (!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, txfee, Params().GetConsensus(), tx.nTime ? tx.nTime : block.nTime, (pindex->pprev? pindex->pprev->nMoneySupply : 0))) {
|
|
2131
2084
|
// Any transaction validation failure in ConnectBlock is a block consensus failure
|
|
2132
2085
|
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
|
|
2133
2086
|
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
|
|
2134
2087
|
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), state.ToString());
|
|
2135
2088
|
}
|
|
2136
|
-
|
|
2089
|
+
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
|
2090
|
+
nValueIn += view.AccessCoin(tx.vin[i].prevout).out.nValue;
|
|
2091
|
+
nValueOut += tx.GetValueOut();
|
|
2092
|
+
if (!tx.IsCoinStake())
|
|
2093
|
+
nFees += txfee;
|
|
2137
2094
|
if (!MoneyRange(nFees)) {
|
|
2138
2095
|
LogPrintf("ERROR: %s: accumulated fee in the block out of range.\n", __func__);
|
|
2139
2096
|
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-txns-accumulated-fee-outofrange");
|
|
@@ -2182,16 +2139,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2182
2139
|
if (i > 0) {
|
|
2183
2140
|
blockundo.vtxundo.push_back(CTxUndo());
|
|
2184
2141
|
}
|
|
2185
|
-
UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
|
|
2142
|
+
UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight, IsProtocolV12(pindex));
|
|
2186
2143
|
}
|
|
2187
2144
|
int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2;
|
|
2188
2145
|
LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal);
|
|
2189
2146
|
|
|
2190
|
-
|
|
2191
|
-
if (block.vtx[0]->GetValueOut() > blockReward) {
|
|
2192
|
-
LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)\n", block.vtx[0]->GetValueOut(), blockReward);
|
|
2193
|
-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-amount");
|
|
2194
|
-
}
|
|
2147
|
+
// peercoin: coinbase reward check relocated to CheckBlock()
|
|
2195
2148
|
|
|
2196
2149
|
if (!control.Wait()) {
|
|
2197
2150
|
LogPrintf("ERROR: %s: CheckQueue failed\n", __func__);
|
|
@@ -2203,6 +2156,15 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2203
2156
|
if (fJustCheck)
|
|
2204
2157
|
return true;
|
|
2205
2158
|
|
|
2159
|
+
// peercoin: track money supply and mint amount info
|
|
2160
|
+
pindex->nMint = nValueOut - nValueIn + nFees;
|
|
2161
|
+
pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn;
|
|
2162
|
+
|
|
2163
|
+
// peercoin: fees are not collected by miners as in bitcoin
|
|
2164
|
+
// peercoin: fees are destroyed to compensate the entire network
|
|
2165
|
+
if (gArgs.GetBoolArg("-printcreation", false))
|
|
2166
|
+
LogPrintf("%s: destroy=%s nFees=%lld\n", __func__, FormatMoney(nFees), nFees);
|
|
2167
|
+
|
|
2206
2168
|
if (!m_blockman.WriteUndoDataForBlock(blockundo, state, pindex, m_params)) {
|
|
2207
2169
|
return false;
|
|
2208
2170
|
}
|
|
@@ -2272,7 +2234,6 @@ bool CChainState::FlushStateToDisk(
|
|
|
2272
2234
|
assert(this->CanFlushToDisk());
|
|
2273
2235
|
static std::chrono::microseconds nLastWrite{0};
|
|
2274
2236
|
static std::chrono::microseconds nLastFlush{0};
|
|
2275
|
-
std::set<int> setFilesToPrune;
|
|
2276
2237
|
bool full_flush_completed = false;
|
|
2277
2238
|
|
|
2278
2239
|
const size_t coins_count = CoinsTip().GetCacheSize();
|
|
@@ -2280,12 +2241,10 @@ bool CChainState::FlushStateToDisk(
|
|
|
2280
2241
|
|
|
2281
2242
|
try {
|
|
2282
2243
|
{
|
|
2283
|
-
bool fFlushForPrune = false;
|
|
2284
2244
|
bool fDoFullFlush = false;
|
|
2285
2245
|
|
|
2286
2246
|
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState();
|
|
2287
2247
|
LOCK(m_blockman.cs_LastBlockFile);
|
|
2288
|
-
if (fPruneMode && (m_blockman.m_check_for_pruning || nManualPruneHeight > 0) && !fReindex) {
|
|
2289
2248
|
// make sure we don't prune above the blockfilterindexes bestblocks
|
|
2290
2249
|
// pruning is height-based
|
|
2291
2250
|
int last_prune = m_chain.Height(); // last height we can prune
|
|
@@ -2293,24 +2252,6 @@ bool CChainState::FlushStateToDisk(
|
|
|
2293
2252
|
last_prune = std::max(1, std::min(last_prune, index.GetSummary().best_block_height));
|
|
2294
2253
|
});
|
|
2295
2254
|
|
|
2296
|
-
if (nManualPruneHeight > 0) {
|
|
2297
|
-
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCH);
|
|
2298
|
-
|
|
2299
|
-
m_blockman.FindFilesToPruneManual(setFilesToPrune, std::min(last_prune, nManualPruneHeight), m_chain.Height());
|
|
2300
|
-
} else {
|
|
2301
|
-
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
|
|
2302
|
-
|
|
2303
|
-
m_blockman.FindFilesToPrune(setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
|
|
2304
|
-
m_blockman.m_check_for_pruning = false;
|
|
2305
|
-
}
|
|
2306
|
-
if (!setFilesToPrune.empty()) {
|
|
2307
|
-
fFlushForPrune = true;
|
|
2308
|
-
if (!fHavePruned) {
|
|
2309
|
-
m_blockman.m_block_tree_db->WriteFlag("prunedblockfiles", true);
|
|
2310
|
-
fHavePruned = true;
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
2255
|
const auto nNow = GetTime<std::chrono::microseconds>();
|
|
2315
2256
|
// Avoid writing/flushing immediately after startup.
|
|
2316
2257
|
if (nLastWrite.count() == 0) {
|
|
@@ -2328,7 +2269,7 @@ bool CChainState::FlushStateToDisk(
|
|
|
2328
2269
|
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
|
|
2329
2270
|
bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + DATABASE_FLUSH_INTERVAL;
|
|
2330
2271
|
// Combine all conditions that result in a full cache flush.
|
|
2331
|
-
fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush
|
|
2272
|
+
fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush;
|
|
2332
2273
|
// Write blocks and block index to disk.
|
|
2333
2274
|
if (fDoFullFlush || fPeriodicWrite) {
|
|
2334
2275
|
// Ensure we can write block index
|
|
@@ -2350,12 +2291,6 @@ bool CChainState::FlushStateToDisk(
|
|
|
2350
2291
|
return AbortNode(state, "Failed to write to block index database");
|
|
2351
2292
|
}
|
|
2352
2293
|
}
|
|
2353
|
-
// Finally remove any pruned files
|
|
2354
|
-
if (fFlushForPrune) {
|
|
2355
|
-
LOG_TIME_MILLIS_WITH_CATEGORY("unlink pruned files", BCLog::BENCH);
|
|
2356
|
-
|
|
2357
|
-
UnlinkPrunedFiles(setFilesToPrune);
|
|
2358
|
-
}
|
|
2359
2294
|
nLastWrite = nNow;
|
|
2360
2295
|
}
|
|
2361
2296
|
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
|
|
@@ -2376,12 +2311,11 @@ bool CChainState::FlushStateToDisk(
|
|
|
2376
2311
|
return AbortNode(state, "Failed to write to coin database");
|
|
2377
2312
|
nLastFlush = nNow;
|
|
2378
2313
|
full_flush_completed = true;
|
|
2379
|
-
|
|
2314
|
+
TRACE4(utxocache, flush,
|
|
2380
2315
|
(int64_t)(GetTimeMicros() - nNow.count()), // in microseconds (µs)
|
|
2381
2316
|
(u_int32_t)mode,
|
|
2382
2317
|
(u_int64_t)coins_count,
|
|
2383
|
-
(u_int64_t)coins_mem_usage
|
|
2384
|
-
(bool)fFlushForPrune);
|
|
2318
|
+
(u_int64_t)coins_mem_usage);
|
|
2385
2319
|
}
|
|
2386
2320
|
}
|
|
2387
2321
|
if (full_flush_completed) {
|
|
@@ -2402,25 +2336,6 @@ void CChainState::ForceFlushStateToDisk()
|
|
|
2402
2336
|
}
|
|
2403
2337
|
}
|
|
2404
2338
|
|
|
2405
|
-
void CChainState::PruneAndFlush()
|
|
2406
|
-
{
|
|
2407
|
-
BlockValidationState state;
|
|
2408
|
-
m_blockman.m_check_for_pruning = true;
|
|
2409
|
-
if (!this->FlushStateToDisk(state, FlushStateMode::NONE)) {
|
|
2410
|
-
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
static void DoWarning(const bilingual_str& warning)
|
|
2415
|
-
{
|
|
2416
|
-
static bool fWarned = false;
|
|
2417
|
-
SetMiscWarning(warning);
|
|
2418
|
-
if (!fWarned) {
|
|
2419
|
-
AlertNotify(warning.original);
|
|
2420
|
-
fWarned = true;
|
|
2421
|
-
}
|
|
2422
|
-
}
|
|
2423
|
-
|
|
2424
2339
|
/** Private helper function that concatenates warning messages. */
|
|
2425
2340
|
static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
|
|
2426
2341
|
{
|
|
@@ -2441,7 +2356,7 @@ static void UpdateTipLog(
|
|
|
2441
2356
|
LogPrintf("%s%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n",
|
|
2442
2357
|
prefix, func_name,
|
|
2443
2358
|
tip->GetBlockHash().ToString(), tip->nHeight, tip->nVersion,
|
|
2444
|
-
log(tip->
|
|
2359
|
+
log(tip->nChainTrust.getdouble()) / log(2.0), (unsigned long)tip->nChainTx,
|
|
2445
2360
|
FormatISO8601DateTime(tip->GetBlockTime()),
|
|
2446
2361
|
GuessVerificationProgress(params.TxData(), tip),
|
|
2447
2362
|
coins_tip.DynamicMemoryUsage() * (1.0 / (1 << 20)),
|
|
@@ -2479,19 +2394,8 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew)
|
|
|
2479
2394
|
bilingual_str warning_messages;
|
|
2480
2395
|
if (!this->IsInitialBlockDownload()) {
|
|
2481
2396
|
const CBlockIndex* pindex = pindexNew;
|
|
2482
|
-
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
|
|
2483
|
-
WarningBitsConditionChecker checker(bit);
|
|
2484
|
-
ThresholdState state = checker.GetStateFor(pindex, m_params.GetConsensus(), warningcache[bit]);
|
|
2485
|
-
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
|
|
2486
|
-
const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
|
|
2487
|
-
if (state == ThresholdState::ACTIVE) {
|
|
2488
|
-
DoWarning(warning);
|
|
2489
|
-
} else {
|
|
2490
|
-
AppendWarning(warning_messages, warning);
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
|
-
}
|
|
2494
2397
|
}
|
|
2398
|
+
|
|
2495
2399
|
UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "", warning_messages.original);
|
|
2496
2400
|
}
|
|
2497
2401
|
|
|
@@ -2704,7 +2608,7 @@ CBlockIndex* CChainState::FindMostWorkChain()
|
|
|
2704
2608
|
bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA);
|
|
2705
2609
|
if (fFailedChain || fMissingData) {
|
|
2706
2610
|
// Candidate chain is not usable (either invalid or missing data)
|
|
2707
|
-
if (fFailedChain && (m_chainman.m_best_invalid == nullptr || pindexNew->
|
|
2611
|
+
if (fFailedChain && (m_chainman.m_best_invalid == nullptr || pindexNew->nChainTrust > m_chainman.m_best_invalid->nChainTrust)) {
|
|
2708
2612
|
m_chainman.m_best_invalid = pindexNew;
|
|
2709
2613
|
}
|
|
2710
2614
|
CBlockIndex *pindexFailed = pindexNew;
|
|
@@ -2815,7 +2719,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
|
|
|
2815
2719
|
}
|
|
2816
2720
|
} else {
|
|
2817
2721
|
PruneBlockIndexCandidates();
|
|
2818
|
-
if (!pindexOldTip || m_chain.Tip()->
|
|
2722
|
+
if (!pindexOldTip || m_chain.Tip()->nChainTrust > pindexOldTip->nChainTrust) {
|
|
2819
2723
|
// We're in a better position than we were. Return temporarily to release the lock.
|
|
2820
2724
|
fContinue = false;
|
|
2821
2725
|
break;
|
|
@@ -2981,15 +2885,15 @@ bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex
|
|
|
2981
2885
|
AssertLockNotHeld(::cs_main);
|
|
2982
2886
|
{
|
|
2983
2887
|
LOCK(cs_main);
|
|
2984
|
-
if (pindex->
|
|
2888
|
+
if (pindex->nChainTrust < m_chain.Tip()->nChainTrust) {
|
|
2985
2889
|
// Nothing to do, this block is not at the tip.
|
|
2986
2890
|
return true;
|
|
2987
2891
|
}
|
|
2988
|
-
if (m_chain.Tip()->
|
|
2892
|
+
if (m_chain.Tip()->nChainTrust > nLastPreciousChainwork) {
|
|
2989
2893
|
// The chain has been extended since the last call, reset the counter.
|
|
2990
2894
|
nBlockReverseSequenceId = -1;
|
|
2991
2895
|
}
|
|
2992
|
-
nLastPreciousChainwork = m_chain.Tip()->
|
|
2896
|
+
nLastPreciousChainwork = m_chain.Tip()->nChainTrust;
|
|
2993
2897
|
setBlockIndexCandidates.erase(pindex);
|
|
2994
2898
|
pindex->nSequenceId = nBlockReverseSequenceId;
|
|
2995
2899
|
if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) {
|
|
@@ -3046,7 +2950,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
|
|
|
3046
2950
|
!CBlockIndexWorkComparator()(candidate, pindex->pprev) &&
|
|
3047
2951
|
candidate->IsValid(BLOCK_VALID_TRANSACTIONS) &&
|
|
3048
2952
|
candidate->HaveTxsDownloaded()) {
|
|
3049
|
-
candidate_blocks_by_work.insert(std::make_pair(candidate->
|
|
2953
|
+
candidate_blocks_by_work.insert(std::make_pair(candidate->nChainTrust, candidate));
|
|
3050
2954
|
}
|
|
3051
2955
|
}
|
|
3052
2956
|
}
|
|
@@ -3096,7 +3000,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
|
|
|
3096
3000
|
}
|
|
3097
3001
|
|
|
3098
3002
|
// Add any equal or more work headers to setBlockIndexCandidates
|
|
3099
|
-
auto candidate_it = candidate_blocks_by_work.lower_bound(invalid_walk_tip->pprev->
|
|
3003
|
+
auto candidate_it = candidate_blocks_by_work.lower_bound(invalid_walk_tip->pprev->nChainTrust);
|
|
3100
3004
|
while (candidate_it != candidate_blocks_by_work.end()) {
|
|
3101
3005
|
if (!CBlockIndexWorkComparator()(candidate_it->second, invalid_walk_tip->pprev)) {
|
|
3102
3006
|
setBlockIndexCandidates.insert(candidate_it->second);
|
|
@@ -3195,7 +3099,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
|
|
|
3195
3099
|
pindexNew->nDataPos = pos.nPos;
|
|
3196
3100
|
pindexNew->nUndoPos = 0;
|
|
3197
3101
|
pindexNew->nStatus |= BLOCK_HAVE_DATA;
|
|
3198
|
-
if (
|
|
3102
|
+
if (pindexNew->pprev && IsBTC16BIPsEnabled(pindexNew->pprev->nTime)) {
|
|
3199
3103
|
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
|
|
3200
3104
|
}
|
|
3201
3105
|
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
|
|
@@ -3239,7 +3143,7 @@ static bool CheckBlockHeader(const CBlockHeader& block, BlockValidationState& st
|
|
|
3239
3143
|
return true;
|
|
3240
3144
|
}
|
|
3241
3145
|
|
|
3242
|
-
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot)
|
|
3146
|
+
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSignature)
|
|
3243
3147
|
{
|
|
3244
3148
|
// These are checks that are independent of context.
|
|
3245
3149
|
|
|
@@ -3248,7 +3152,7 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
|
|
|
3248
3152
|
|
|
3249
3153
|
// Check that the header is valid (particularly PoW). This is mostly
|
|
3250
3154
|
// redundant with the call in AcceptBlockHeader.
|
|
3251
|
-
if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW))
|
|
3155
|
+
if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW && !block.IsProofOfStake()))
|
|
3252
3156
|
return false;
|
|
3253
3157
|
|
|
3254
3158
|
// Signet only: check block solution
|
|
@@ -3287,6 +3191,32 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
|
|
|
3287
3191
|
if (block.vtx[i]->IsCoinBase())
|
|
3288
3192
|
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-multiple", "more than one coinbase");
|
|
3289
3193
|
|
|
3194
|
+
// peercoin: only the second transaction can be the optional coinstake
|
|
3195
|
+
for (unsigned int i = 2; i < block.vtx.size(); i++)
|
|
3196
|
+
if (block.vtx[i]->IsCoinStake())
|
|
3197
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cs-missing", "coinstake in wrong position");
|
|
3198
|
+
|
|
3199
|
+
// peercoin: first coinbase output should be empty if proof-of-stake block
|
|
3200
|
+
if (block.IsProofOfStake() && !block.vtx[0]->vout[0].IsEmpty())
|
|
3201
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-notempty", "coinbase output not empty in PoS block");
|
|
3202
|
+
|
|
3203
|
+
// Check coinbase timestamp
|
|
3204
|
+
if (block.GetBlockTime() > (block.vtx[0]->nTime ? (int64_t)block.vtx[0]->nTime : block.GetBlockTime()) + (IsProtocolV09(block.GetBlockTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9))
|
|
3205
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-time", "coinbase timestamp is too early");
|
|
3206
|
+
|
|
3207
|
+
// Check coinstake timestamp
|
|
3208
|
+
if (block.IsProofOfStake() && !CheckCoinStakeTimestamp(block.GetBlockTime(), block.vtx[1]->nTime ? (int64_t)block.vtx[1]->nTime : block.GetBlockTime()))
|
|
3209
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cs-time", "coinstake timestamp violation");
|
|
3210
|
+
|
|
3211
|
+
// Check coinbase reward
|
|
3212
|
+
CAmount nCoinbaseCost = 0;
|
|
3213
|
+
if (block.IsProofOfWork())
|
|
3214
|
+
nCoinbaseCost = (GetMinFee(*block.vtx[0], block.nTime) < PERKB_TX_FEE)? 0 : (GetMinFee(*block.vtx[0], block.nTime) - PERKB_TX_FEE);
|
|
3215
|
+
if (block.vtx[0]->GetValueOut() > (block.IsProofOfWork()? (GetProofOfWorkReward(block.nBits, block.GetBlockTime()) - nCoinbaseCost) : 0))
|
|
3216
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-amount",
|
|
3217
|
+
strprintf("CheckBlock() : coinbase reward exceeded %s > %s",
|
|
3218
|
+
FormatMoney(block.vtx[0]->GetValueOut()),
|
|
3219
|
+
FormatMoney(block.IsProofOfWork()? GetProofOfWorkReward(block.nBits, block.GetBlockTime()) : 0)));
|
|
3290
3220
|
// Check transactions
|
|
3291
3221
|
// Must check for duplicate inputs (see CVE-2018-17144)
|
|
3292
3222
|
for (const auto& tx : block.vtx) {
|
|
@@ -3297,6 +3227,9 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
|
|
|
3297
3227
|
assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS);
|
|
3298
3228
|
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(),
|
|
3299
3229
|
strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), tx_state.GetDebugMessage()));
|
|
3230
|
+
// peercoin: check transaction timestamp
|
|
3231
|
+
if (block.GetBlockTime() < (int64_t)tx->nTime)
|
|
3232
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-tx-time", strprintf("%s : block timestamp earlier than transaction timestamp", __func__));
|
|
3300
3233
|
}
|
|
3301
3234
|
}
|
|
3302
3235
|
unsigned int nSigOps = 0;
|
|
@@ -3310,6 +3243,12 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
|
|
|
3310
3243
|
if (fCheckPOW && fCheckMerkleRoot)
|
|
3311
3244
|
block.fChecked = true;
|
|
3312
3245
|
|
|
3246
|
+
// peercoin: check block signature
|
|
3247
|
+
// Only check block signature if check merkle root, c.f. commit 3cd01fdf
|
|
3248
|
+
// rfc6: validate signatures of proof of stake blocks only after 0.8 fork
|
|
3249
|
+
if (fCheckMerkleRoot && fCheckSignature && (block.IsProofOfStake() || !IsBTC16BIPsEnabled(block.GetBlockTime())) && !CheckBlockSignature(block))
|
|
3250
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sign", strprintf("%s : bad block signature", __func__));
|
|
3251
|
+
|
|
3313
3252
|
return true;
|
|
3314
3253
|
}
|
|
3315
3254
|
|
|
@@ -3317,7 +3256,7 @@ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPr
|
|
|
3317
3256
|
{
|
|
3318
3257
|
int commitpos = GetWitnessCommitmentIndex(block);
|
|
3319
3258
|
static const std::vector<unsigned char> nonce(32, 0x00);
|
|
3320
|
-
if (commitpos != NO_WITNESS_COMMITMENT &&
|
|
3259
|
+
if (commitpos != NO_WITNESS_COMMITMENT && IsBTC16BIPsEnabled(pindexPrev->nTime) && !block.vtx[0]->HasWitness()) {
|
|
3321
3260
|
CMutableTransaction tx(*block.vtx[0]);
|
|
3322
3261
|
tx.vin[0].scriptWitness.stack.resize(1);
|
|
3323
3262
|
tx.vin[0].scriptWitness.stack[0] = nonce;
|
|
@@ -3367,10 +3306,10 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
|
|
3367
3306
|
assert(pindexPrev != nullptr);
|
|
3368
3307
|
const int nHeight = pindexPrev->nHeight + 1;
|
|
3369
3308
|
|
|
3370
|
-
// Check proof of work
|
|
3309
|
+
// Check proof of work or proof-of-stake
|
|
3371
3310
|
const Consensus::Params& consensusParams = params.GetConsensus();
|
|
3372
|
-
if (block.nBits !=
|
|
3373
|
-
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-diffbits", "incorrect proof of work");
|
|
3311
|
+
if (block.nBits != GetNextTargetRequired(pindexPrev, block.nFlags & CBlockIndex::BLOCK_PROOF_OF_STAKE, consensusParams))
|
|
3312
|
+
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-diffbits", "incorrect proof of work/stake");
|
|
3374
3313
|
|
|
3375
3314
|
// Check against checkpoints
|
|
3376
3315
|
if (fCheckpointsEnabled) {
|
|
@@ -3385,20 +3324,19 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
|
|
3385
3324
|
}
|
|
3386
3325
|
|
|
3387
3326
|
// Check timestamp against prev
|
|
3388
|
-
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
|
|
3327
|
+
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast() || block.GetBlockTime() + (IsProtocolV09(block.GetBlockTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9) < pindexPrev->GetBlockTime())
|
|
3389
3328
|
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-too-old", "block's timestamp is too early");
|
|
3390
3329
|
|
|
3391
3330
|
// Check timestamp
|
|
3392
|
-
if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME)
|
|
3331
|
+
if (block.GetBlockTime() > nAdjustedTime + (IsProtocolV09(block.GetBlockTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9))
|
|
3393
3332
|
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future");
|
|
3394
3333
|
|
|
3395
|
-
// Reject blocks
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
(block.nVersion < 4 &&
|
|
3334
|
+
// Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
|
|
3335
|
+
// check for version 2, 3 and 4 upgrades
|
|
3336
|
+
if ((block.nVersion < 2 && IsProtocolV06(pindexPrev)) ||
|
|
3337
|
+
(block.nVersion < 4 && IsProtocolV12(pindexPrev)))
|
|
3399
3338
|
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, strprintf("bad-version(0x%08x)", block.nVersion),
|
|
3400
3339
|
strprintf("rejected nVersion=0x%08x block", block.nVersion));
|
|
3401
|
-
}
|
|
3402
3340
|
|
|
3403
3341
|
return true;
|
|
3404
3342
|
}
|
|
@@ -3409,13 +3347,13 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
|
|
3409
3347
|
* in ConnectBlock().
|
|
3410
3348
|
* Note that -reindex-chainstate skips the validation that happens here!
|
|
3411
3349
|
*/
|
|
3412
|
-
static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& state, const
|
|
3350
|
+
static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& state, const CBlockIndex* pindexPrev)
|
|
3413
3351
|
{
|
|
3414
3352
|
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
|
|
3415
3353
|
|
|
3416
3354
|
// Enforce BIP113 (Median Time Past).
|
|
3417
3355
|
int nLockTimeFlags = 0;
|
|
3418
|
-
if (
|
|
3356
|
+
if (pindexPrev && IsBTC16BIPsEnabled(pindexPrev->nTime)) {
|
|
3419
3357
|
assert(pindexPrev != nullptr);
|
|
3420
3358
|
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
|
|
3421
3359
|
}
|
|
@@ -3432,7 +3370,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
|
|
|
3432
3370
|
}
|
|
3433
3371
|
|
|
3434
3372
|
// Enforce rule that the coinbase starts with serialized block height
|
|
3435
|
-
if (
|
|
3373
|
+
if (pindexPrev && IsProtocolV06(pindexPrev) && block.nVersion >= 2)
|
|
3436
3374
|
{
|
|
3437
3375
|
CScript expect = CScript() << nHeight;
|
|
3438
3376
|
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
|
|
@@ -3450,7 +3388,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
|
|
|
3450
3388
|
// {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness reserved value). In case there are
|
|
3451
3389
|
// multiple, the last one is used.
|
|
3452
3390
|
bool fHaveWitness = false;
|
|
3453
|
-
if (
|
|
3391
|
+
if (pindexPrev && IsBTC16BIPsEnabled(pindexPrev->nTime)) {
|
|
3454
3392
|
int commitpos = GetWitnessCommitmentIndex(block);
|
|
3455
3393
|
if (commitpos != NO_WITNESS_COMMITMENT) {
|
|
3456
3394
|
bool malleated = false;
|
|
@@ -3510,7 +3448,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
|
|
|
3510
3448
|
return true;
|
|
3511
3449
|
}
|
|
3512
3450
|
|
|
3513
|
-
if (!CheckBlockHeader(block, state, chainparams.GetConsensus())) {
|
|
3451
|
+
if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), !(block.nFlags & CBlockIndex::BLOCK_PROOF_OF_STAKE))) {
|
|
3514
3452
|
LogPrint(BCLog::VALIDATION, "%s: Consensus::CheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
|
|
3515
3453
|
return false;
|
|
3516
3454
|
}
|
|
@@ -3580,22 +3518,39 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
|
|
|
3580
3518
|
}
|
|
3581
3519
|
|
|
3582
3520
|
// Exposed wrapper for AcceptBlockHeader
|
|
3583
|
-
bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
|
|
3521
|
+
bool ChainstateManager::ProcessNewBlockHeaders(int32_t& nPoSTemperature, const uint256& lastAcceptedHeader, const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
|
|
3584
3522
|
{
|
|
3585
3523
|
AssertLockNotHeld(cs_main);
|
|
3586
3524
|
{
|
|
3587
3525
|
LOCK(cs_main);
|
|
3526
|
+
|
|
3527
|
+
int nCooling = POW_HEADER_COOLING;
|
|
3528
|
+
if (headers[0].hashPrevBlock != lastAcceptedHeader && !lastAcceptedHeader.IsNull()) {
|
|
3529
|
+
nPoSTemperature += (18 + headers.size()) / 10;
|
|
3530
|
+
nCooling = 0;
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3588
3533
|
for (const CBlockHeader& header : headers) {
|
|
3534
|
+
bool fPoS = header.nFlags & CBlockIndex::BLOCK_PROOF_OF_STAKE;
|
|
3535
|
+
|
|
3589
3536
|
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
|
|
3590
3537
|
bool accepted{AcceptBlockHeader(header, state, chainparams, &pindex)};
|
|
3591
3538
|
ActiveChainstate().CheckBlockIndex();
|
|
3592
3539
|
|
|
3593
3540
|
if (!accepted) {
|
|
3541
|
+
nPoSTemperature += POW_HEADER_COOLING;
|
|
3594
3542
|
return false;
|
|
3595
3543
|
}
|
|
3596
3544
|
if (ppindex) {
|
|
3597
3545
|
*ppindex = pindex;
|
|
3598
3546
|
}
|
|
3547
|
+
|
|
3548
|
+
if (fPoS) {
|
|
3549
|
+
nPoSTemperature++;
|
|
3550
|
+
} else { // PoW
|
|
3551
|
+
nPoSTemperature -= nCooling;
|
|
3552
|
+
nPoSTemperature = std::max((int)nPoSTemperature, 0);
|
|
3553
|
+
}
|
|
3599
3554
|
}
|
|
3600
3555
|
}
|
|
3601
3556
|
if (NotifyHeaderTip(ActiveChainstate())) {
|
|
@@ -3613,6 +3568,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
|
|
|
3613
3568
|
bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock)
|
|
3614
3569
|
{
|
|
3615
3570
|
const CBlock& block = *pblock;
|
|
3571
|
+
bool fCheckPoS = true;
|
|
3616
3572
|
|
|
3617
3573
|
if (fNewBlock) *fNewBlock = false;
|
|
3618
3574
|
AssertLockHeld(cs_main);
|
|
@@ -3626,11 +3582,17 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
|
|
|
3626
3582
|
if (!accepted_header)
|
|
3627
3583
|
return false;
|
|
3628
3584
|
|
|
3585
|
+
// peercoin: we should only accept blocks that can be connected to a prev block with validated PoS
|
|
3586
|
+
if (fCheckPoS && pindex->pprev && !pindex->pprev->IsValid(BLOCK_VALID_TRANSACTIONS)) {
|
|
3587
|
+
return error("%s: this block does not connect to any valid known block", __func__);
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3629
3590
|
// Try to process all requested blocks that we don't have, but only
|
|
3630
3591
|
// process an unrequested block if it's new and has enough work to
|
|
3631
3592
|
// advance our tip, and isn't too many blocks ahead.
|
|
3632
3593
|
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
|
|
3633
|
-
bool fHasMoreOrSameWork = (m_chain.Tip() ? pindex->
|
|
3594
|
+
bool fHasMoreOrSameWork = (m_chain.Tip() ? pindex->nChainTrust >= m_chain.Tip()->nChainTrust : true);
|
|
3595
|
+
|
|
3634
3596
|
// Blocks that are too out-of-order needlessly limit the effectiveness of
|
|
3635
3597
|
// pruning, because pruning will not delete block files that contain any
|
|
3636
3598
|
// blocks which are too close in height to the tip. Apply this test
|
|
@@ -3656,11 +3618,11 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
|
|
|
3656
3618
|
// If our tip is behind, a peer could try to send us
|
|
3657
3619
|
// low-work blocks on a fake chain that we would never
|
|
3658
3620
|
// request; don't process these.
|
|
3659
|
-
if (pindex->
|
|
3621
|
+
if (pindex->nChainTrust < nMinimumChainWork) return true;
|
|
3660
3622
|
}
|
|
3661
3623
|
|
|
3662
3624
|
if (!CheckBlock(block, state, m_params.GetConsensus()) ||
|
|
3663
|
-
!ContextualCheckBlock(block, state,
|
|
3625
|
+
!ContextualCheckBlock(block, state, pindex->pprev)) {
|
|
3664
3626
|
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
|
|
3665
3627
|
pindex->nStatus |= BLOCK_FAILED_VALID;
|
|
3666
3628
|
m_blockman.m_dirty_blockindex.insert(pindex);
|
|
@@ -3668,9 +3630,16 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
|
|
|
3668
3630
|
return error("%s: %s", __func__, state.ToString());
|
|
3669
3631
|
}
|
|
3670
3632
|
|
|
3633
|
+
// peercoin: check PoS
|
|
3634
|
+
if (fCheckPoS && !PeercoinContextualBlockChecks(block, state, pindex, false, m_chainman.ActiveChainstate())) {
|
|
3635
|
+
pindex->nStatus |= BLOCK_FAILED_VALID;
|
|
3636
|
+
m_blockman.m_dirty_blockindex.insert(pindex);
|
|
3637
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-pos", "proof of stake is incorrect");
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3671
3640
|
// Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW
|
|
3672
3641
|
// (but if it does not build on our best tip, let the SendMessages loop relay it)
|
|
3673
|
-
if (!IsInitialBlockDownload() && m_chain.Tip() == pindex->pprev)
|
|
3642
|
+
if (!m_chainman.ActiveChainstate().IsInitialBlockDownload() && m_chain.Tip() == pindex->pprev)
|
|
3674
3643
|
GetMainSignals().NewPoWValidBlock(pindex, pblock);
|
|
3675
3644
|
|
|
3676
3645
|
// Write block to history file
|
|
@@ -3687,19 +3656,19 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
|
|
|
3687
3656
|
}
|
|
3688
3657
|
|
|
3689
3658
|
FlushStateToDisk(state, FlushStateMode::NONE);
|
|
3690
|
-
|
|
3691
3659
|
CheckBlockIndex();
|
|
3692
3660
|
|
|
3693
3661
|
return true;
|
|
3694
3662
|
}
|
|
3695
3663
|
|
|
3696
|
-
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
|
|
3664
|
+
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block, CBlockIndex** ppindex, bool* fPoSDuplicate)
|
|
3697
3665
|
{
|
|
3698
3666
|
AssertLockNotHeld(cs_main);
|
|
3699
3667
|
|
|
3700
3668
|
{
|
|
3701
3669
|
CBlockIndex *pindex = nullptr;
|
|
3702
3670
|
if (new_block) *new_block = false;
|
|
3671
|
+
if (fPoSDuplicate) *fPoSDuplicate = false;
|
|
3703
3672
|
BlockValidationState state;
|
|
3704
3673
|
|
|
3705
3674
|
// CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
|
|
@@ -3716,10 +3685,19 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
|
|
|
3716
3685
|
// Store to disk
|
|
3717
3686
|
ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block);
|
|
3718
3687
|
}
|
|
3688
|
+
if (ppindex)
|
|
3689
|
+
*ppindex = ret ? pindex : nullptr;
|
|
3719
3690
|
if (!ret) {
|
|
3720
3691
|
GetMainSignals().BlockChecked(*block, state);
|
|
3721
3692
|
return error("%s: AcceptBlock FAILED (%s)", __func__, state.ToString());
|
|
3722
3693
|
}
|
|
3694
|
+
|
|
3695
|
+
if (pindex->IsProofOfStake() && !ActiveChainstate().IsInitialBlockDownload()) {
|
|
3696
|
+
int32_t ndx = univHash(pindex->hashProofOfStake);
|
|
3697
|
+
if (fPoSDuplicate && vStakeSeen[ndx] == pindex->hashProofOfStake)
|
|
3698
|
+
*fPoSDuplicate = true;
|
|
3699
|
+
vStakeSeen[ndx] = pindex->hashProofOfStake;
|
|
3700
|
+
}
|
|
3723
3701
|
}
|
|
3724
3702
|
|
|
3725
3703
|
NotifyHeaderTip(ActiveChainstate());
|
|
@@ -3768,7 +3746,7 @@ bool TestBlockValidity(BlockValidationState& state,
|
|
|
3768
3746
|
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
|
|
3769
3747
|
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
|
|
3770
3748
|
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
|
|
3771
|
-
if (!ContextualCheckBlock(block, state,
|
|
3749
|
+
if (!ContextualCheckBlock(block, state, pindexPrev))
|
|
3772
3750
|
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, state.ToString());
|
|
3773
3751
|
if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, true)) {
|
|
3774
3752
|
return false;
|
|
@@ -3778,16 +3756,6 @@ bool TestBlockValidity(BlockValidationState& state,
|
|
|
3778
3756
|
return true;
|
|
3779
3757
|
}
|
|
3780
3758
|
|
|
3781
|
-
/* This function is called from the RPC code for pruneblockchain */
|
|
3782
|
-
void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight)
|
|
3783
|
-
{
|
|
3784
|
-
BlockValidationState state;
|
|
3785
|
-
if (!active_chainstate.FlushStateToDisk(
|
|
3786
|
-
state, FlushStateMode::NONE, nManualPruneHeight)) {
|
|
3787
|
-
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
|
|
3788
|
-
}
|
|
3789
|
-
}
|
|
3790
|
-
|
|
3791
3759
|
void CChainState::LoadMempool(const ArgsManager& args)
|
|
3792
3760
|
{
|
|
3793
3761
|
if (!m_mempool) return;
|
|
@@ -3874,10 +3842,10 @@ bool CVerifyDB::VerifyDB(
|
|
|
3874
3842
|
if (pindex->nHeight <= chainstate.m_chain.Height() - nCheckDepth) {
|
|
3875
3843
|
break;
|
|
3876
3844
|
}
|
|
3877
|
-
if (
|
|
3878
|
-
// If
|
|
3845
|
+
if (is_snapshot_cs && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
|
|
3846
|
+
// If running under an assumeutxo snapshot, only go
|
|
3879
3847
|
// back as far as we have data.
|
|
3880
|
-
LogPrintf("VerifyDB(): block verification stopping at height %d (
|
|
3848
|
+
LogPrintf("VerifyDB(): block verification stopping at height %d (no data)\n", pindex->nHeight);
|
|
3881
3849
|
break;
|
|
3882
3850
|
}
|
|
3883
3851
|
CBlock block;
|
|
@@ -3968,7 +3936,7 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i
|
|
|
3968
3936
|
}
|
|
3969
3937
|
}
|
|
3970
3938
|
// Pass check = true as every addition may be an overwrite.
|
|
3971
|
-
AddCoins(inputs, *tx, pindex->nHeight, true);
|
|
3939
|
+
AddCoins(inputs, *tx, pindex->nHeight, true, IsProtocolV12(pindex));
|
|
3972
3940
|
}
|
|
3973
3941
|
return true;
|
|
3974
3942
|
}
|
|
@@ -4047,7 +4015,7 @@ bool CChainState::NeedsRedownload() const
|
|
|
4047
4015
|
// At and above m_params.SegwitHeight, segwit consensus rules must be validated
|
|
4048
4016
|
CBlockIndex* block{m_chain.Tip()};
|
|
4049
4017
|
|
|
4050
|
-
while (block != nullptr &&
|
|
4018
|
+
while (block != nullptr && block->pprev && IsBTC16BIPsEnabled(block->pprev->nTime)) {
|
|
4051
4019
|
if (!(block->nStatus & BLOCK_OPT_WITNESS)) {
|
|
4052
4020
|
// block is insufficiently validated for a segwit client
|
|
4053
4021
|
return true;
|
|
@@ -4074,11 +4042,6 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
|
|
|
4074
4042
|
chainman.Unload();
|
|
4075
4043
|
pindexBestHeader = nullptr;
|
|
4076
4044
|
if (mempool) mempool->clear();
|
|
4077
|
-
g_versionbitscache.Clear();
|
|
4078
|
-
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
|
|
4079
|
-
warningcache[b].clear();
|
|
4080
|
-
}
|
|
4081
|
-
fHavePruned = false;
|
|
4082
4045
|
}
|
|
4083
4046
|
|
|
4084
4047
|
bool ChainstateManager::LoadBlockIndex()
|
|
@@ -4330,16 +4293,8 @@ void CChainState::CheckBlockIndex()
|
|
|
4330
4293
|
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
|
|
4331
4294
|
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
|
|
4332
4295
|
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
if (!fHavePruned && !pindex->IsAssumedValid()) {
|
|
4336
|
-
// If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0
|
|
4337
|
-
assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0));
|
|
4338
|
-
assert(pindexFirstMissing == pindexFirstNeverProcessed);
|
|
4339
|
-
} else {
|
|
4340
|
-
// If we have pruned, then we can only say that HAVE_DATA implies nTx > 0
|
|
4341
|
-
if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0);
|
|
4342
|
-
}
|
|
4296
|
+
assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0));
|
|
4297
|
+
assert(pindexFirstMissing == pindexFirstNeverProcessed);
|
|
4343
4298
|
if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA);
|
|
4344
4299
|
if (pindex->IsAssumedValid()) {
|
|
4345
4300
|
// Assumed-valid blocks should have some nTx value.
|
|
@@ -4355,7 +4310,7 @@ void CChainState::CheckBlockIndex()
|
|
|
4355
4310
|
assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded());
|
|
4356
4311
|
assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded());
|
|
4357
4312
|
assert(pindex->nHeight == nHeight); // nHeight must be consistent.
|
|
4358
|
-
assert(pindex->pprev == nullptr || pindex->
|
|
4313
|
+
assert(pindex->pprev == nullptr || pindex->nChainTrust >= pindex->pprev->nChainTrust); // For every block except the genesis block, the chainwork must be larger than the parent's.
|
|
4359
4314
|
assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks.
|
|
4360
4315
|
assert(pindexFirstNotTreeValid == nullptr); // All m_blockman.m_block_index entries must at least be TREE valid
|
|
4361
4316
|
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == nullptr); // TREE valid implies all parents are TREE valid
|
|
@@ -4371,8 +4326,7 @@ void CChainState::CheckBlockIndex()
|
|
|
4371
4326
|
|
|
4372
4327
|
// If this block sorts at least as good as the current tip and
|
|
4373
4328
|
// is valid and we have all data for its parents, it must be in
|
|
4374
|
-
// setBlockIndexCandidates.
|
|
4375
|
-
// even if some data has been pruned.
|
|
4329
|
+
// setBlockIndexCandidates. m_chain.Tip() must also be there.
|
|
4376
4330
|
//
|
|
4377
4331
|
// Don't perform this check for the background chainstate since
|
|
4378
4332
|
// its setBlockIndexCandidates shouldn't have some entries (i.e. those past the
|
|
@@ -4404,23 +4358,6 @@ void CChainState::CheckBlockIndex()
|
|
|
4404
4358
|
}
|
|
4405
4359
|
if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in m_blocks_unlinked if we don't HAVE_DATA
|
|
4406
4360
|
if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in m_blocks_unlinked.
|
|
4407
|
-
if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) {
|
|
4408
|
-
// We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent.
|
|
4409
|
-
assert(fHavePruned); // We must have pruned.
|
|
4410
|
-
// This block may have entered m_blocks_unlinked if:
|
|
4411
|
-
// - it has a descendant that at some point had more work than the
|
|
4412
|
-
// tip, and
|
|
4413
|
-
// - we tried switching to that descendant but were missing
|
|
4414
|
-
// data for some intermediate block between m_chain and the
|
|
4415
|
-
// tip.
|
|
4416
|
-
// So if this block is itself better than m_chain.Tip() and it wasn't in
|
|
4417
|
-
// setBlockIndexCandidates, then it must be in m_blocks_unlinked.
|
|
4418
|
-
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && setBlockIndexCandidates.count(pindex) == 0) {
|
|
4419
|
-
if (pindexFirstInvalid == nullptr) {
|
|
4420
|
-
assert(foundInUnlinked);
|
|
4421
|
-
}
|
|
4422
|
-
}
|
|
4423
|
-
}
|
|
4424
4361
|
// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow
|
|
4425
4362
|
// End: actual consistency checks.
|
|
4426
4363
|
|
|
@@ -4678,6 +4615,126 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
|
|
|
4678
4615
|
return std::min<double>(pindex->nChainTx / fTxTotal, 1.0);
|
|
4679
4616
|
}
|
|
4680
4617
|
|
|
4618
|
+
|
|
4619
|
+
|
|
4620
|
+
|
|
4621
|
+
|
|
4622
|
+
// peercoin: total coin age spent in transaction, in the unit of coin-days.
|
|
4623
|
+
// Only those coins meeting minimum age requirement counts. As those
|
|
4624
|
+
// transactions not in main chain are not currently indexed so we
|
|
4625
|
+
// might not find out about their coin age. Older transactions are
|
|
4626
|
+
// guaranteed to be in main chain by sync-checkpoint. This rule is
|
|
4627
|
+
// introduced to help nodes establish a consistent view of the coin
|
|
4628
|
+
// age (trust score) of competing branches.
|
|
4629
|
+
bool GetCoinAge(const CTransaction& tx, const CCoinsViewCache &view, uint64_t& nCoinAge, unsigned int nTimeTx, bool isTrueCoinAge)
|
|
4630
|
+
{
|
|
4631
|
+
arith_uint256 bnCentSecond = 0; // coin age in the unit of cent-seconds
|
|
4632
|
+
nCoinAge = 0;
|
|
4633
|
+
|
|
4634
|
+
if (tx.IsCoinBase())
|
|
4635
|
+
return true;
|
|
4636
|
+
|
|
4637
|
+
// Transaction index is required to get to block header
|
|
4638
|
+
if (!g_txindex)
|
|
4639
|
+
return false; // Transaction index not available
|
|
4640
|
+
|
|
4641
|
+
for (const auto& txin : tx.vin)
|
|
4642
|
+
{
|
|
4643
|
+
// First try finding the previous transaction in database
|
|
4644
|
+
const COutPoint &prevout = txin.prevout;
|
|
4645
|
+
Coin coin;
|
|
4646
|
+
|
|
4647
|
+
if (isTrueCoinAge && !view.GetCoin(prevout, coin))
|
|
4648
|
+
continue; // previous transaction not in main chain
|
|
4649
|
+
if (nTimeTx < coin.nTime)
|
|
4650
|
+
return false; // Transaction timestamp violation
|
|
4651
|
+
|
|
4652
|
+
CDiskTxPos postx;
|
|
4653
|
+
CBlockHeader header;
|
|
4654
|
+
CTransactionRef txPrev;
|
|
4655
|
+
auto it = g_txindex->cachedTxs.find(prevout.hash);
|
|
4656
|
+
if (it != g_txindex->cachedTxs.end()) {
|
|
4657
|
+
header = it->second.first;
|
|
4658
|
+
txPrev = it->second.second;
|
|
4659
|
+
} else {
|
|
4660
|
+
if (g_txindex->FindTxPosition(prevout.hash, postx)) {
|
|
4661
|
+
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
|
4662
|
+
try {
|
|
4663
|
+
file >> header;
|
|
4664
|
+
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
|
|
4665
|
+
file >> txPrev;
|
|
4666
|
+
} catch (std::exception &e) {
|
|
4667
|
+
return error("%s() : deserialize or I/O error in GetCoinAge()", __PRETTY_FUNCTION__);
|
|
4668
|
+
}
|
|
4669
|
+
} else
|
|
4670
|
+
return error("%s() : tx missing in tx index in GetCoinAge()", __PRETTY_FUNCTION__);
|
|
4671
|
+
g_txindex->cachedTxs[prevout.hash] = std::pair(header,txPrev);
|
|
4672
|
+
}
|
|
4673
|
+
|
|
4674
|
+
if (txPrev->GetHash() != prevout.hash)
|
|
4675
|
+
return error("%s() : txid mismatch in GetCoinAge()", __PRETTY_FUNCTION__);
|
|
4676
|
+
|
|
4677
|
+
if (header.GetBlockTime() + Params().GetConsensus().nStakeMinAge > nTimeTx)
|
|
4678
|
+
continue; // only count coins meeting min age requirement
|
|
4679
|
+
|
|
4680
|
+
int64_t nValueIn = txPrev->vout[txin.prevout.n].nValue;
|
|
4681
|
+
int nEffectiveAge = nTimeTx-(txPrev->nTime ? txPrev->nTime : header.GetBlockTime());
|
|
4682
|
+
|
|
4683
|
+
if (!isTrueCoinAge || IsProtocolV09(nTimeTx))
|
|
4684
|
+
nEffectiveAge = std::min(nEffectiveAge, 365 * 24 * 60 * 60);
|
|
4685
|
+
|
|
4686
|
+
bnCentSecond += arith_uint256(nValueIn) * nEffectiveAge / CENT;
|
|
4687
|
+
|
|
4688
|
+
if (gArgs.GetBoolArg("-printcoinage", false))
|
|
4689
|
+
LogPrintf("coin age nValueIn=%-12lld nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nEffectiveAge, bnCentSecond.ToString());
|
|
4690
|
+
}
|
|
4691
|
+
|
|
4692
|
+
arith_uint256 bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60);
|
|
4693
|
+
if (gArgs.GetBoolArg("-printcoinage", false))
|
|
4694
|
+
LogPrintf("coin age bnCoinDay=%s\n", bnCoinDay.ToString());
|
|
4695
|
+
nCoinAge = bnCoinDay.GetLow64();
|
|
4696
|
+
return true;
|
|
4697
|
+
}
|
|
4698
|
+
|
|
4699
|
+
// peercoin: sign block
|
|
4700
|
+
typedef std::vector<unsigned char> valtype;
|
|
4701
|
+
bool SignBlock(CBlock& block, const CWallet& keystore)
|
|
4702
|
+
{
|
|
4703
|
+
std::vector<valtype> vSolutions;
|
|
4704
|
+
const CTxOut& txout = block.IsProofOfStake()? block.vtx[1]->vout[1] : block.vtx[0]->vout[0];
|
|
4705
|
+
|
|
4706
|
+
if (Solver(txout.scriptPubKey, vSolutions) != TxoutType::PUBKEY)
|
|
4707
|
+
return false;
|
|
4708
|
+
|
|
4709
|
+
// Sign
|
|
4710
|
+
const valtype& vchPubKey = vSolutions[0];
|
|
4711
|
+
CKey key;
|
|
4712
|
+
if (!keystore.GetLegacyScriptPubKeyMan()->GetKey(CKeyID(Hash160(vchPubKey)), key))
|
|
4713
|
+
return false;
|
|
4714
|
+
if (key.GetPubKey() != CPubKey(vchPubKey))
|
|
4715
|
+
return false;
|
|
4716
|
+
return key.Sign(block.GetHash(), block.vchBlockSig, 0);
|
|
4717
|
+
}
|
|
4718
|
+
|
|
4719
|
+
// peercoin: check block signature
|
|
4720
|
+
bool CheckBlockSignature(const CBlock& block)
|
|
4721
|
+
{
|
|
4722
|
+
if (block.GetHash() == Params().GetConsensus().hashGenesisBlock)
|
|
4723
|
+
return block.vchBlockSig.empty();
|
|
4724
|
+
|
|
4725
|
+
std::vector<valtype> vSolutions;
|
|
4726
|
+
const CTxOut& txout = block.IsProofOfStake()? block.vtx[1]->vout[1] : block.vtx[0]->vout[0];
|
|
4727
|
+
|
|
4728
|
+
if (Solver(txout.scriptPubKey, vSolutions) != TxoutType::PUBKEY)
|
|
4729
|
+
return false;
|
|
4730
|
+
|
|
4731
|
+
const valtype& vchPubKey = vSolutions[0];
|
|
4732
|
+
CPubKey key(vchPubKey);
|
|
4733
|
+
if (block.vchBlockSig.empty())
|
|
4734
|
+
return false;
|
|
4735
|
+
return key.Verify(block.GetHash(), block.vchBlockSig);
|
|
4736
|
+
}
|
|
4737
|
+
|
|
4681
4738
|
std::optional<uint256> ChainstateManager::SnapshotBlockhash() const
|
|
4682
4739
|
{
|
|
4683
4740
|
LOCK(::cs_main);
|
|
@@ -5009,7 +5066,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
|
|
|
5009
5066
|
|
|
5010
5067
|
// Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
|
|
5011
5068
|
// won't ask to rewind the entire assumed-valid chain on startup.
|
|
5012
|
-
if (
|
|
5069
|
+
if (IsBTC16BIPsEnabled(index->nTime)) {
|
|
5013
5070
|
index->nStatus |= BLOCK_OPT_WITNESS;
|
|
5014
5071
|
}
|
|
5015
5072
|
|
|
@@ -16,16 +16,17 @@
|
|
|
16
16
|
#include <consensus/amount.h>
|
|
17
17
|
#include <fs.h>
|
|
18
18
|
#include <node/blockstorage.h>
|
|
19
|
-
#include <policy/feerate.h>
|
|
20
19
|
#include <policy/packages.h>
|
|
21
20
|
#include <script/script_error.h>
|
|
22
21
|
#include <sync.h>
|
|
22
|
+
#include <chain.h>
|
|
23
23
|
#include <txdb.h>
|
|
24
24
|
#include <txmempool.h> // For CTxMemPool::cs
|
|
25
25
|
#include <uint256.h>
|
|
26
26
|
#include <util/check.h>
|
|
27
27
|
#include <util/hasher.h>
|
|
28
28
|
#include <util/translation.h>
|
|
29
|
+
#include <wallet/wallet.h>
|
|
29
30
|
|
|
30
31
|
#include <atomic>
|
|
31
32
|
#include <map>
|
|
@@ -41,8 +42,14 @@
|
|
|
41
42
|
class CChainState;
|
|
42
43
|
class CBlockTreeDB;
|
|
43
44
|
class CChainParams;
|
|
45
|
+
namespace wallet {
|
|
46
|
+
class CWallet;
|
|
47
|
+
} // namespace wallet
|
|
48
|
+
//class CWallet;
|
|
49
|
+
using wallet::CWallet;
|
|
44
50
|
class CTxMemPool;
|
|
45
51
|
class ChainstateManager;
|
|
52
|
+
class CKeyStore;
|
|
46
53
|
struct ChainTxData;
|
|
47
54
|
struct DisconnectedBlockTransactions;
|
|
48
55
|
struct PrecomputedTransactionData;
|
|
@@ -52,8 +59,6 @@ namespace node {
|
|
|
52
59
|
class SnapshotMetadata;
|
|
53
60
|
} // namespace node
|
|
54
61
|
|
|
55
|
-
/** Default for -minrelaytxfee, minimum relay fee for transactions */
|
|
56
|
-
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
|
|
57
62
|
/** Default for -limitancestorcount, max number of in-mempool ancestors */
|
|
58
63
|
static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25;
|
|
59
64
|
/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */
|
|
@@ -80,7 +85,7 @@ static const int MAX_SCRIPTCHECK_THREADS = 15;
|
|
|
80
85
|
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
|
|
81
86
|
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
|
|
82
87
|
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
|
|
83
|
-
static const bool DEFAULT_TXINDEX =
|
|
88
|
+
static const bool DEFAULT_TXINDEX = true; // peercoin: txindex is required for PoS calculations (might change in the future)
|
|
84
89
|
static constexpr bool DEFAULT_COINSTATSINDEX{false};
|
|
85
90
|
static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
|
|
86
91
|
/** Default for -persistmempool */
|
|
@@ -120,8 +125,7 @@ extern bool g_parallel_script_checks;
|
|
|
120
125
|
extern bool fRequireStandard;
|
|
121
126
|
extern bool fCheckBlockIndex;
|
|
122
127
|
extern bool fCheckpointsEnabled;
|
|
123
|
-
|
|
124
|
-
extern CFeeRate minRelayTxFee;
|
|
128
|
+
extern bool fAlerts;
|
|
125
129
|
/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
|
|
126
130
|
extern int64_t nMaxTipAge;
|
|
127
131
|
|
|
@@ -134,9 +138,11 @@ extern arith_uint256 nMinimumChainWork;
|
|
|
134
138
|
/** Best header we've seen so far (used for getheaders queries' starting points). */
|
|
135
139
|
extern CBlockIndex *pindexBestHeader;
|
|
136
140
|
|
|
141
|
+
|
|
137
142
|
/** Documentation for argument 'checklevel'. */
|
|
138
143
|
extern const std::vector<std::string> CHECKLEVEL_DOC;
|
|
139
144
|
|
|
145
|
+
|
|
140
146
|
/** Unload database information */
|
|
141
147
|
void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman);
|
|
142
148
|
/** Run instances of script checking worker threads */
|
|
@@ -144,15 +150,12 @@ void StartScriptCheckWorkerThreads(int threads_num);
|
|
|
144
150
|
/** Stop all of the script checking worker threads */
|
|
145
151
|
void StopScriptCheckWorkerThreads();
|
|
146
152
|
|
|
147
|
-
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
|
|
148
153
|
|
|
149
154
|
bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str{});
|
|
150
155
|
|
|
151
156
|
/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */
|
|
152
157
|
double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pindex);
|
|
153
158
|
|
|
154
|
-
/** Prune block files up to a given height */
|
|
155
|
-
void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight);
|
|
156
159
|
|
|
157
160
|
/**
|
|
158
161
|
* Validation result for a single transaction mempool acceptance.
|
|
@@ -272,7 +275,6 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, const CTr
|
|
|
272
275
|
PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool,
|
|
273
276
|
const Package& txns, bool test_accept)
|
|
274
277
|
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
275
|
-
|
|
276
278
|
/** Transaction validation functions */
|
|
277
279
|
|
|
278
280
|
/**
|
|
@@ -351,7 +353,7 @@ void InitScriptExecutionCache();
|
|
|
351
353
|
/** Functions for validating blocks and updating the block tree */
|
|
352
354
|
|
|
353
355
|
/** Context-independent validity checks */
|
|
354
|
-
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
|
|
356
|
+
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true, bool fCheckSignature = true);
|
|
355
357
|
|
|
356
358
|
/** Check a block is completely valid from start to finish (only works on top of our current best block) */
|
|
357
359
|
bool TestBlockValidity(BlockValidationState& state,
|
|
@@ -624,10 +626,6 @@ public:
|
|
|
624
626
|
//! Unconditionally flush all changes to disk.
|
|
625
627
|
void ForceFlushStateToDisk();
|
|
626
628
|
|
|
627
|
-
//! Prune blockfiles from the disk if necessary and then flush chainstate changes
|
|
628
|
-
//! if we pruned.
|
|
629
|
-
void PruneAndFlush();
|
|
630
|
-
|
|
631
629
|
/**
|
|
632
630
|
* Find the best known block, and make it the tip of the block chain. The
|
|
633
631
|
* result is either failure or an activated best chain. pblock is either
|
|
@@ -961,7 +959,7 @@ public:
|
|
|
961
959
|
* @param[out] new_block A boolean which is set to indicate if the block was first received via this call
|
|
962
960
|
* @returns If the block was processed, independently of block validity
|
|
963
961
|
*/
|
|
964
|
-
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block) LOCKS_EXCLUDED(cs_main);
|
|
962
|
+
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block, CBlockIndex** ppindex = nullptr, bool* fPoSDuplicate = nullptr) LOCKS_EXCLUDED(cs_main);
|
|
965
963
|
|
|
966
964
|
/**
|
|
967
965
|
* Process incoming block headers.
|
|
@@ -974,7 +972,7 @@ public:
|
|
|
974
972
|
* @param[in] chainparams The params for the chain we want to connect to
|
|
975
973
|
* @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers
|
|
976
974
|
*/
|
|
977
|
-
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
|
|
975
|
+
bool ProcessNewBlockHeaders(int32_t& nPoSTemperature, const uint256& lastAcceptedHeader, const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
|
|
978
976
|
|
|
979
977
|
/**
|
|
980
978
|
* Try to add a transaction to the memory pool.
|
|
@@ -1013,6 +1011,12 @@ bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function = fsbri
|
|
|
1013
1011
|
/** Load the mempool from disk. */
|
|
1014
1012
|
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function = fsbridge::fopen);
|
|
1015
1013
|
|
|
1014
|
+
// peercoin:
|
|
1015
|
+
CAmount GetProofOfWorkReward(unsigned int nBits, uint32_t nTime);
|
|
1016
|
+
CAmount GetProofOfStakeReward(int64_t nCoinAge, uint32_t nTime, uint64_t nMoneySupply);
|
|
1017
|
+
bool GetCoinAge(const CTransaction& tx, const CCoinsViewCache &view, uint64_t& nCoinAge, unsigned int nTimeTx, bool isTrueCoinAge = true); // peercoin: get transaction coin age
|
|
1018
|
+
bool SignBlock(CBlock& block, const CWallet& keystore);
|
|
1019
|
+
bool CheckBlockSignature(const CBlock& block);
|
|
1016
1020
|
/**
|
|
1017
1021
|
* Return the expected assumeutxo value for a given height, if one exists.
|
|
1018
1022
|
*
|
|
@@ -109,7 +109,6 @@ protected:
|
|
|
109
109
|
* - SIZELIMIT (removed in size limiting if the mempool exceeds -maxmempool megabytes)
|
|
110
110
|
* - REORG (removed during a reorg)
|
|
111
111
|
* - CONFLICT (removed because it conflicts with in-block transaction)
|
|
112
|
-
* - REPLACED (removed due to RBF replacement)
|
|
113
112
|
*
|
|
114
113
|
* This does not fire for transactions that are removed from the mempool
|
|
115
114
|
* because they have been included in a block. Any client that is interested
|
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
* network protocol versioning
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
static const int PROTOCOL_VERSION =
|
|
12
|
+
static const int PROTOCOL_VERSION = 70017;
|
|
13
|
+
static const int OLD_VERSION = 70014; // peercoin: used to communicate with clients that don't know how to send PoS information in headers
|
|
13
14
|
|
|
14
15
|
//! initial proto version, to be increased after version/verack negotiation
|
|
15
16
|
static const int INIT_PROTO_VERSION = 209;
|
|
16
17
|
|
|
17
18
|
//! disconnect from peers older than this proto version
|
|
18
|
-
static const int MIN_PEER_PROTO_VERSION =
|
|
19
|
+
static const int MIN_PEER_PROTO_VERSION = 70016;
|
|
19
20
|
|
|
20
21
|
//! BIP 0031, pong message, is enabled for all versions AFTER this one
|
|
21
22
|
static const int BIP0031_VERSION = 60000;
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2016-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#include <versionbits.h>
|
|
6
|
-
#include <consensus/params.h>
|
|
7
|
-
|
|
8
|
-
ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
|
|
9
|
-
{
|
|
10
|
-
int nPeriod = Period(params);
|
|
11
|
-
int nThreshold = Threshold(params);
|
|
12
|
-
int min_activation_height = MinActivationHeight(params);
|
|
13
|
-
int64_t nTimeStart = BeginTime(params);
|
|
14
|
-
int64_t nTimeTimeout = EndTime(params);
|
|
15
|
-
|
|
16
|
-
// Check if this deployment is always active.
|
|
17
|
-
if (nTimeStart == Consensus::BIP9Deployment::ALWAYS_ACTIVE) {
|
|
18
|
-
return ThresholdState::ACTIVE;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Check if this deployment is never active.
|
|
22
|
-
if (nTimeStart == Consensus::BIP9Deployment::NEVER_ACTIVE) {
|
|
23
|
-
return ThresholdState::FAILED;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
|
|
27
|
-
if (pindexPrev != nullptr) {
|
|
28
|
-
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Walk backwards in steps of nPeriod to find a pindexPrev whose information is known
|
|
32
|
-
std::vector<const CBlockIndex*> vToCompute;
|
|
33
|
-
while (cache.count(pindexPrev) == 0) {
|
|
34
|
-
if (pindexPrev == nullptr) {
|
|
35
|
-
// The genesis block is by definition defined.
|
|
36
|
-
cache[pindexPrev] = ThresholdState::DEFINED;
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
if (pindexPrev->GetMedianTimePast() < nTimeStart) {
|
|
40
|
-
// Optimization: don't recompute down further, as we know every earlier block will be before the start time
|
|
41
|
-
cache[pindexPrev] = ThresholdState::DEFINED;
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
vToCompute.push_back(pindexPrev);
|
|
45
|
-
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// At this point, cache[pindexPrev] is known
|
|
49
|
-
assert(cache.count(pindexPrev));
|
|
50
|
-
ThresholdState state = cache[pindexPrev];
|
|
51
|
-
|
|
52
|
-
// Now walk forward and compute the state of descendants of pindexPrev
|
|
53
|
-
while (!vToCompute.empty()) {
|
|
54
|
-
ThresholdState stateNext = state;
|
|
55
|
-
pindexPrev = vToCompute.back();
|
|
56
|
-
vToCompute.pop_back();
|
|
57
|
-
|
|
58
|
-
switch (state) {
|
|
59
|
-
case ThresholdState::DEFINED: {
|
|
60
|
-
if (pindexPrev->GetMedianTimePast() >= nTimeStart) {
|
|
61
|
-
stateNext = ThresholdState::STARTED;
|
|
62
|
-
}
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
case ThresholdState::STARTED: {
|
|
66
|
-
// We need to count
|
|
67
|
-
const CBlockIndex* pindexCount = pindexPrev;
|
|
68
|
-
int count = 0;
|
|
69
|
-
for (int i = 0; i < nPeriod; i++) {
|
|
70
|
-
if (Condition(pindexCount, params)) {
|
|
71
|
-
count++;
|
|
72
|
-
}
|
|
73
|
-
pindexCount = pindexCount->pprev;
|
|
74
|
-
}
|
|
75
|
-
if (count >= nThreshold) {
|
|
76
|
-
stateNext = ThresholdState::LOCKED_IN;
|
|
77
|
-
} else if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
|
|
78
|
-
stateNext = ThresholdState::FAILED;
|
|
79
|
-
}
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
case ThresholdState::LOCKED_IN: {
|
|
83
|
-
// Progresses into ACTIVE provided activation height will have been reached.
|
|
84
|
-
if (pindexPrev->nHeight + 1 >= min_activation_height) {
|
|
85
|
-
stateNext = ThresholdState::ACTIVE;
|
|
86
|
-
}
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
case ThresholdState::FAILED:
|
|
90
|
-
case ThresholdState::ACTIVE: {
|
|
91
|
-
// Nothing happens, these are terminal states.
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
cache[pindexPrev] = state = stateNext;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return state;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, std::vector<bool>* signalling_blocks) const
|
|
102
|
-
{
|
|
103
|
-
BIP9Stats stats = {};
|
|
104
|
-
|
|
105
|
-
stats.period = Period(params);
|
|
106
|
-
stats.threshold = Threshold(params);
|
|
107
|
-
|
|
108
|
-
if (pindex == nullptr) return stats;
|
|
109
|
-
|
|
110
|
-
// Find how many blocks are in the current period
|
|
111
|
-
int blocks_in_period = 1 + (pindex->nHeight % stats.period);
|
|
112
|
-
|
|
113
|
-
// Reset signalling_blocks
|
|
114
|
-
if (signalling_blocks) {
|
|
115
|
-
signalling_blocks->assign(blocks_in_period, false);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Count from current block to beginning of period
|
|
119
|
-
int elapsed = 0;
|
|
120
|
-
int count = 0;
|
|
121
|
-
const CBlockIndex* currentIndex = pindex;
|
|
122
|
-
do {
|
|
123
|
-
++elapsed;
|
|
124
|
-
--blocks_in_period;
|
|
125
|
-
if (Condition(currentIndex, params)) {
|
|
126
|
-
++count;
|
|
127
|
-
if (signalling_blocks) signalling_blocks->at(blocks_in_period) = true;
|
|
128
|
-
}
|
|
129
|
-
currentIndex = currentIndex->pprev;
|
|
130
|
-
} while(blocks_in_period > 0);
|
|
131
|
-
|
|
132
|
-
stats.elapsed = elapsed;
|
|
133
|
-
stats.count = count;
|
|
134
|
-
stats.possible = (stats.period - stats.threshold ) >= (stats.elapsed - count);
|
|
135
|
-
|
|
136
|
-
return stats;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
|
|
140
|
-
{
|
|
141
|
-
int64_t start_time = BeginTime(params);
|
|
142
|
-
if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE || start_time == Consensus::BIP9Deployment::NEVER_ACTIVE) {
|
|
143
|
-
return 0;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const ThresholdState initialState = GetStateFor(pindexPrev, params, cache);
|
|
147
|
-
|
|
148
|
-
// BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment."
|
|
149
|
-
if (initialState == ThresholdState::DEFINED) {
|
|
150
|
-
return 0;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const int nPeriod = Period(params);
|
|
154
|
-
|
|
155
|
-
// A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
|
|
156
|
-
// To ease understanding of the following height calculation, it helps to remember that
|
|
157
|
-
// right now pindexPrev points to the block prior to the block that we are computing for, thus:
|
|
158
|
-
// if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and
|
|
159
|
-
// if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period.
|
|
160
|
-
// The parent of the genesis block is represented by nullptr.
|
|
161
|
-
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
|
|
162
|
-
|
|
163
|
-
const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
|
|
164
|
-
|
|
165
|
-
while (previousPeriodParent != nullptr && GetStateFor(previousPeriodParent, params, cache) == initialState) {
|
|
166
|
-
pindexPrev = previousPeriodParent;
|
|
167
|
-
previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Adjust the result because right now we point to the parent block.
|
|
171
|
-
return pindexPrev->nHeight + 1;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
namespace
|
|
175
|
-
{
|
|
176
|
-
/**
|
|
177
|
-
* Class to implement versionbits logic.
|
|
178
|
-
*/
|
|
179
|
-
class VersionBitsConditionChecker : public AbstractThresholdConditionChecker {
|
|
180
|
-
private:
|
|
181
|
-
const Consensus::DeploymentPos id;
|
|
182
|
-
|
|
183
|
-
protected:
|
|
184
|
-
int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; }
|
|
185
|
-
int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; }
|
|
186
|
-
int MinActivationHeight(const Consensus::Params& params) const override { return params.vDeployments[id].min_activation_height; }
|
|
187
|
-
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
|
|
188
|
-
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
|
|
189
|
-
|
|
190
|
-
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
|
|
191
|
-
{
|
|
192
|
-
return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
public:
|
|
196
|
-
explicit VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {}
|
|
197
|
-
uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; }
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
} // namespace
|
|
201
|
-
|
|
202
|
-
ThresholdState VersionBitsCache::State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
|
|
203
|
-
{
|
|
204
|
-
LOCK(m_mutex);
|
|
205
|
-
return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, m_caches[pos]);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
BIP9Stats VersionBitsCache::Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos, std::vector<bool>* signalling_blocks)
|
|
209
|
-
{
|
|
210
|
-
return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindex, params, signalling_blocks);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
int VersionBitsCache::StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
|
|
214
|
-
{
|
|
215
|
-
LOCK(m_mutex);
|
|
216
|
-
return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, m_caches[pos]);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
uint32_t VersionBitsCache::Mask(const Consensus::Params& params, Consensus::DeploymentPos pos)
|
|
220
|
-
{
|
|
221
|
-
return VersionBitsConditionChecker(pos).Mask(params);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
int32_t VersionBitsCache::ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
|
|
225
|
-
{
|
|
226
|
-
LOCK(m_mutex);
|
|
227
|
-
int32_t nVersion = VERSIONBITS_TOP_BITS;
|
|
228
|
-
|
|
229
|
-
for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
|
|
230
|
-
Consensus::DeploymentPos pos = static_cast<Consensus::DeploymentPos>(i);
|
|
231
|
-
ThresholdState state = VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, m_caches[pos]);
|
|
232
|
-
if (state == ThresholdState::LOCKED_IN || state == ThresholdState::STARTED) {
|
|
233
|
-
nVersion |= Mask(params, pos);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return nVersion;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
void VersionBitsCache::Clear()
|
|
241
|
-
{
|
|
242
|
-
LOCK(m_mutex);
|
|
243
|
-
for (unsigned int d = 0; d < Consensus::MAX_VERSION_BITS_DEPLOYMENTS; d++) {
|
|
244
|
-
m_caches[d].clear();
|
|
245
|
-
}
|
|
246
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2016-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#ifndef BITCOIN_VERSIONBITS_H
|
|
6
|
-
#define BITCOIN_VERSIONBITS_H
|
|
7
|
-
|
|
8
|
-
#include <chain.h>
|
|
9
|
-
#include <sync.h>
|
|
10
|
-
|
|
11
|
-
#include <map>
|
|
12
|
-
|
|
13
|
-
/** What block version to use for new blocks (pre versionbits) */
|
|
14
|
-
static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4;
|
|
15
|
-
/** What bits to set in version for versionbits blocks */
|
|
16
|
-
static const int32_t VERSIONBITS_TOP_BITS = 0x20000000UL;
|
|
17
|
-
/** What bitmask determines whether versionbits is in use */
|
|
18
|
-
static const int32_t VERSIONBITS_TOP_MASK = 0xE0000000UL;
|
|
19
|
-
/** Total bits available for versionbits */
|
|
20
|
-
static const int32_t VERSIONBITS_NUM_BITS = 29;
|
|
21
|
-
|
|
22
|
-
/** BIP 9 defines a finite-state-machine to deploy a softfork in multiple stages.
|
|
23
|
-
* State transitions happen during retarget period if conditions are met
|
|
24
|
-
* In case of reorg, transitions can go backward. Without transition, state is
|
|
25
|
-
* inherited between periods. All blocks of a period share the same state.
|
|
26
|
-
*/
|
|
27
|
-
enum class ThresholdState {
|
|
28
|
-
DEFINED, // First state that each softfork starts out as. The genesis block is by definition in this state for each deployment.
|
|
29
|
-
STARTED, // For blocks past the starttime.
|
|
30
|
-
LOCKED_IN, // For at least one retarget period after the first retarget period with STARTED blocks of which at least threshold have the associated bit set in nVersion, until min_activation_height is reached.
|
|
31
|
-
ACTIVE, // For all blocks after the LOCKED_IN retarget period (final state)
|
|
32
|
-
FAILED, // For all blocks once the first retarget period after the timeout time is hit, if LOCKED_IN wasn't already reached (final state)
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// A map that gives the state for blocks whose height is a multiple of Period().
|
|
36
|
-
// The map is indexed by the block's parent, however, so all keys in the map
|
|
37
|
-
// will either be nullptr or a block with (height + 1) % Period() == 0.
|
|
38
|
-
typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache;
|
|
39
|
-
|
|
40
|
-
/** Display status of an in-progress BIP9 softfork */
|
|
41
|
-
struct BIP9Stats {
|
|
42
|
-
/** Length of blocks of the BIP9 signalling period */
|
|
43
|
-
int period;
|
|
44
|
-
/** Number of blocks with the version bit set required to activate the softfork */
|
|
45
|
-
int threshold;
|
|
46
|
-
/** Number of blocks elapsed since the beginning of the current period */
|
|
47
|
-
int elapsed;
|
|
48
|
-
/** Number of blocks with the version bit set since the beginning of the current period */
|
|
49
|
-
int count;
|
|
50
|
-
/** False if there are not enough blocks left in this period to pass activation threshold */
|
|
51
|
-
bool possible;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Abstract class that implements BIP9-style threshold logic, and caches results.
|
|
56
|
-
*/
|
|
57
|
-
class AbstractThresholdConditionChecker {
|
|
58
|
-
protected:
|
|
59
|
-
virtual bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const =0;
|
|
60
|
-
virtual int64_t BeginTime(const Consensus::Params& params) const =0;
|
|
61
|
-
virtual int64_t EndTime(const Consensus::Params& params) const =0;
|
|
62
|
-
virtual int MinActivationHeight(const Consensus::Params& params) const { return 0; }
|
|
63
|
-
virtual int Period(const Consensus::Params& params) const =0;
|
|
64
|
-
virtual int Threshold(const Consensus::Params& params) const =0;
|
|
65
|
-
|
|
66
|
-
public:
|
|
67
|
-
/** Returns the numerical statistics of an in-progress BIP9 softfork in the period including pindex
|
|
68
|
-
* If provided, signalling_blocks is set to true/false based on whether each block in the period signalled
|
|
69
|
-
*/
|
|
70
|
-
BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, std::vector<bool>* signalling_blocks = nullptr) const;
|
|
71
|
-
/** Returns the state for pindex A based on parent pindexPrev B. Applies any state transition if conditions are present.
|
|
72
|
-
* Caches state from first block of period. */
|
|
73
|
-
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
|
|
74
|
-
/** Returns the height since when the ThresholdState has started for pindex A based on parent pindexPrev B, all blocks of a period share the same */
|
|
75
|
-
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
/** BIP 9 allows multiple softforks to be deployed in parallel. We cache
|
|
79
|
-
* per-period state for every one of them. */
|
|
80
|
-
class VersionBitsCache
|
|
81
|
-
{
|
|
82
|
-
private:
|
|
83
|
-
Mutex m_mutex;
|
|
84
|
-
ThresholdConditionCache m_caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] GUARDED_BY(m_mutex);
|
|
85
|
-
|
|
86
|
-
public:
|
|
87
|
-
/** Get the numerical statistics for a given deployment for the signalling period that includes pindex.
|
|
88
|
-
* If provided, signalling_blocks is set to true/false based on whether each block in the period signalled
|
|
89
|
-
*/
|
|
90
|
-
static BIP9Stats Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos, std::vector<bool>* signalling_blocks = nullptr);
|
|
91
|
-
|
|
92
|
-
static uint32_t Mask(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
|
93
|
-
|
|
94
|
-
/** Get the BIP9 state for a given deployment for the block after pindexPrev. */
|
|
95
|
-
ThresholdState State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
|
|
96
|
-
|
|
97
|
-
/** Get the block height at which the BIP9 deployment switched into the state for the block after pindexPrev. */
|
|
98
|
-
int StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
|
|
99
|
-
|
|
100
|
-
/** Determine what nVersion a new block should use
|
|
101
|
-
*/
|
|
102
|
-
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params);
|
|
103
|
-
|
|
104
|
-
void Clear();
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
#endif // BITCOIN_VERSIONBITS_H
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
#define BITCOIN_WALLET_COINCONTROL_H
|
|
7
7
|
|
|
8
8
|
#include <outputtype.h>
|
|
9
|
-
#include <policy/feerate.h>
|
|
10
|
-
#include <policy/fees.h>
|
|
11
9
|
#include <primitives/transaction.h>
|
|
12
10
|
#include <script/keyorigin.h>
|
|
13
11
|
#include <script/signingprovider.h>
|
|
@@ -41,20 +39,12 @@ public:
|
|
|
41
39
|
bool fAllowOtherInputs = false;
|
|
42
40
|
//! Includes watch only addresses which are solvable
|
|
43
41
|
bool fAllowWatchOnly = false;
|
|
44
|
-
//! Override automatic min/max checks on fee, m_feerate must be set if true
|
|
45
|
-
bool fOverrideFeeRate = false;
|
|
46
|
-
//! Override the wallet's m_pay_tx_fee if set
|
|
47
|
-
std::optional<CFeeRate> m_feerate;
|
|
48
42
|
//! Override the default confirmation target if set
|
|
49
43
|
std::optional<unsigned int> m_confirm_target;
|
|
50
|
-
//! Override the wallet's m_signal_rbf if set
|
|
51
|
-
std::optional<bool> m_signal_bip125_rbf;
|
|
52
44
|
//! Avoid partial use of funds sent to a given address
|
|
53
45
|
bool m_avoid_partial_spends = DEFAULT_AVOIDPARTIALSPENDS;
|
|
54
46
|
//! Forbids inclusion of dirty (previously used) addresses
|
|
55
47
|
bool m_avoid_address_reuse = false;
|
|
56
|
-
//! Fee estimation mode to control arguments to estimateSmartFee
|
|
57
|
-
FeeEstimateMode m_fee_mode = FeeEstimateMode::UNSET;
|
|
58
48
|
//! Minimum chain depth value for coin availability
|
|
59
49
|
int m_min_depth = DEFAULT_MIN_DEPTH;
|
|
60
50
|
//! Maximum chain depth value for coin availability
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
#include <wallet/coinselection.h>
|
|
6
6
|
|
|
7
7
|
#include <consensus/amount.h>
|
|
8
|
-
#include <
|
|
8
|
+
#include <consensus/tx_verify.h>
|
|
9
|
+
#include <timedata.h>
|
|
9
10
|
#include <util/check.h>
|
|
10
11
|
#include <util/system.h>
|
|
11
12
|
#include <util/moneystr.h>
|
|
@@ -94,7 +95,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
|
|
|
94
95
|
bool backtrack = false;
|
|
95
96
|
if (curr_value + curr_available_value < selection_target || // Cannot possibly reach target with the amount remaining in the curr_available_value.
|
|
96
97
|
curr_value > selection_target + cost_of_change || // Selected value is out of range, go back and try other branch
|
|
97
|
-
(curr_waste > best_waste
|
|
98
|
+
(curr_waste > best_waste)) { // Don't select things which we know will be more wasteful if the waste is increasing
|
|
98
99
|
backtrack = true;
|
|
99
100
|
} else if (curr_value >= selection_target) { // Selected value is within range
|
|
100
101
|
curr_waste += (curr_value - selection_target); // This is the excess value which is added to the waste for the below comparison
|
|
@@ -130,7 +131,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
|
|
|
130
131
|
curr_selection.back() = false;
|
|
131
132
|
OutputGroup& utxo = utxo_pool.at(curr_selection.size() - 1);
|
|
132
133
|
curr_value -= utxo.GetSelectionAmount();
|
|
133
|
-
curr_waste -= utxo.fee
|
|
134
|
+
curr_waste -= utxo.fee;
|
|
134
135
|
} else { // Moving forwards, continuing down this branch
|
|
135
136
|
OutputGroup& utxo = utxo_pool.at(curr_selection.size());
|
|
136
137
|
|
|
@@ -147,7 +148,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
|
|
|
147
148
|
// Inclusion branch first (Largest First Exploration)
|
|
148
149
|
curr_selection.push_back(true);
|
|
149
150
|
curr_value += utxo.GetSelectionAmount();
|
|
150
|
-
curr_waste += utxo.fee
|
|
151
|
+
curr_waste += utxo.fee;
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
}
|
|
@@ -250,7 +251,7 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
|
|
|
250
251
|
if (group.GetSelectionAmount() == nTargetValue) {
|
|
251
252
|
result.AddInput(group);
|
|
252
253
|
return result;
|
|
253
|
-
} else if (group.GetSelectionAmount() < nTargetValue +
|
|
254
|
+
} else if (group.GetSelectionAmount() < nTargetValue + MIN_TXOUT_AMOUNT) {
|
|
254
255
|
applicable_groups.push_back(group);
|
|
255
256
|
nTotalLower += group.GetSelectionAmount();
|
|
256
257
|
} else if (!lowest_larger || group.GetSelectionAmount() < lowest_larger->GetSelectionAmount()) {
|
|
@@ -277,14 +278,14 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
|
|
|
277
278
|
CAmount nBest;
|
|
278
279
|
|
|
279
280
|
ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue, vfBest, nBest);
|
|
280
|
-
if (nBest != nTargetValue && nTotalLower >= nTargetValue +
|
|
281
|
-
ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue +
|
|
281
|
+
if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_TXOUT_AMOUNT) {
|
|
282
|
+
ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue + MIN_TXOUT_AMOUNT, vfBest, nBest);
|
|
282
283
|
}
|
|
283
284
|
|
|
284
285
|
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
|
|
285
286
|
// or the next bigger coin is closer), return the bigger coin
|
|
286
287
|
if (lowest_larger &&
|
|
287
|
-
((nBest != nTargetValue && nBest < nTargetValue +
|
|
288
|
+
((nBest != nTargetValue && nBest < nTargetValue + MIN_TXOUT_AMOUNT) || lowest_larger->GetSelectionAmount() <= nBest)) {
|
|
288
289
|
result.AddInput(*lowest_larger);
|
|
289
290
|
} else {
|
|
290
291
|
for (unsigned int i = 0; i < applicable_groups.size(); i++) {
|
|
@@ -315,7 +316,7 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
|
|
|
315
316
|
|
|
316
317
|
void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants, bool positive_only) {
|
|
317
318
|
// Compute the effective value first
|
|
318
|
-
const CAmount coin_fee = output.m_input_bytes < 0 ? 0 :
|
|
319
|
+
const CAmount coin_fee = output.m_input_bytes < 0 ? 0 : GetMinFee(output.m_input_bytes, GetAdjustedTime());
|
|
319
320
|
const CAmount ev = output.txout.nValue - coin_fee;
|
|
320
321
|
|
|
321
322
|
// Filter for positive only here before adding the coin
|
|
@@ -327,8 +328,7 @@ void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size
|
|
|
327
328
|
coin.m_fee = coin_fee;
|
|
328
329
|
fee += coin.m_fee;
|
|
329
330
|
|
|
330
|
-
coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 :
|
|
331
|
-
long_term_fee += coin.m_long_term_fee;
|
|
331
|
+
coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : GetMinFee(output.m_input_bytes, GetAdjustedTime());
|
|
332
332
|
|
|
333
333
|
coin.effective_value = ev;
|
|
334
334
|
effective_value += coin.effective_value;
|
|
@@ -6,17 +6,14 @@
|
|
|
6
6
|
#define BITCOIN_WALLET_COINSELECTION_H
|
|
7
7
|
|
|
8
8
|
#include <consensus/amount.h>
|
|
9
|
-
#include <policy/feerate.h>
|
|
10
9
|
#include <primitives/transaction.h>
|
|
11
10
|
#include <random.h>
|
|
12
11
|
|
|
13
12
|
#include <optional>
|
|
14
13
|
|
|
15
14
|
namespace wallet {
|
|
16
|
-
//! target minimum change amount
|
|
17
|
-
static constexpr CAmount MIN_CHANGE{COIN / 100};
|
|
18
15
|
//! final minimum change amount after paying for fees
|
|
19
|
-
static const CAmount MIN_FINAL_CHANGE =
|
|
16
|
+
static const CAmount MIN_FINAL_CHANGE = MIN_TXOUT_AMOUNT;
|
|
20
17
|
|
|
21
18
|
/** A UTXO under consideration for use in funding a new transaction. */
|
|
22
19
|
class CInputCoin {
|
|
@@ -83,13 +80,6 @@ struct CoinSelectionParams
|
|
|
83
80
|
CAmount m_change_fee{0};
|
|
84
81
|
/** Cost of creating the change output + cost of spending the change output in the future. */
|
|
85
82
|
CAmount m_cost_of_change{0};
|
|
86
|
-
/** The targeted feerate of the transaction being built. */
|
|
87
|
-
CFeeRate m_effective_feerate;
|
|
88
|
-
/** The feerate estimate used to estimate an upper bound on what should be sufficient to spend
|
|
89
|
-
* the change output sometime in the future. */
|
|
90
|
-
CFeeRate m_long_term_feerate;
|
|
91
|
-
/** If the cost to spend a change output at the discard feerate exceeds its value, drop it to fees. */
|
|
92
|
-
CFeeRate m_discard_feerate;
|
|
93
83
|
/** Size of the transaction before coin selection, consisting of the header and recipient
|
|
94
84
|
* output(s), excluding the inputs and change output(s). */
|
|
95
85
|
size_t tx_noinputs_size = 0;
|
|
@@ -100,13 +90,10 @@ struct CoinSelectionParams
|
|
|
100
90
|
* reuse. Dust outputs are not eligible to be added to output groups and thus not considered. */
|
|
101
91
|
bool m_avoid_partial_spends = false;
|
|
102
92
|
|
|
103
|
-
CoinSelectionParams(size_t change_output_size, size_t change_spend_size,
|
|
104
|
-
|
|
93
|
+
CoinSelectionParams(size_t change_output_size, size_t change_spend_size,
|
|
94
|
+
size_t tx_noinputs_size, bool avoid_partial) :
|
|
105
95
|
change_output_size(change_output_size),
|
|
106
96
|
change_spend_size(change_spend_size),
|
|
107
|
-
m_effective_feerate(effective_feerate),
|
|
108
|
-
m_long_term_feerate(long_term_feerate),
|
|
109
|
-
m_discard_feerate(discard_feerate),
|
|
110
97
|
tx_noinputs_size(tx_noinputs_size),
|
|
111
98
|
m_avoid_partial_spends(avoid_partial)
|
|
112
99
|
{}
|
|
@@ -157,22 +144,12 @@ struct OutputGroup
|
|
|
157
144
|
CAmount effective_value{0};
|
|
158
145
|
/** The fee to spend these UTXOs at the effective feerate. */
|
|
159
146
|
CAmount fee{0};
|
|
160
|
-
/** The target feerate of the transaction we're trying to build. */
|
|
161
|
-
CFeeRate m_effective_feerate{0};
|
|
162
|
-
/** The fee to spend these UTXOs at the long term feerate. */
|
|
163
|
-
CAmount long_term_fee{0};
|
|
164
|
-
/** The feerate for spending a created change output eventually (i.e. not urgently, and thus at
|
|
165
|
-
* a lower feerate). Calculated using long term fee estimate. This is used to decide whether
|
|
166
|
-
* it could be economical to create a change output. */
|
|
167
|
-
CFeeRate m_long_term_feerate{0};
|
|
168
147
|
/** Indicate that we are subtracting the fee from outputs.
|
|
169
148
|
* When true, the value that is used for coin selection is the UTXO's real value rather than effective value */
|
|
170
149
|
bool m_subtract_fee_outputs{false};
|
|
171
150
|
|
|
172
151
|
OutputGroup() {}
|
|
173
152
|
OutputGroup(const CoinSelectionParams& params) :
|
|
174
|
-
m_effective_feerate(params.m_effective_feerate),
|
|
175
|
-
m_long_term_feerate(params.m_long_term_feerate),
|
|
176
153
|
m_subtract_fee_outputs(params.m_subtract_fee_outputs)
|
|
177
154
|
{}
|
|
178
155
|
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2017-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#include <interfaces/chain.h>
|
|
6
|
-
#include <policy/fees.h>
|
|
7
|
-
#include <policy/policy.h>
|
|
8
|
-
#include <util/moneystr.h>
|
|
9
|
-
#include <util/rbf.h>
|
|
10
|
-
#include <util/system.h>
|
|
11
|
-
#include <util/translation.h>
|
|
12
|
-
#include <wallet/coincontrol.h>
|
|
13
|
-
#include <wallet/feebumper.h>
|
|
14
|
-
#include <wallet/fees.h>
|
|
15
|
-
#include <wallet/receive.h>
|
|
16
|
-
#include <wallet/spend.h>
|
|
17
|
-
#include <wallet/wallet.h>
|
|
18
|
-
|
|
19
|
-
namespace wallet {
|
|
20
|
-
//! Check whether transaction has descendant in wallet or mempool, or has been
|
|
21
|
-
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
|
|
22
|
-
static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
|
|
23
|
-
{
|
|
24
|
-
if (wallet.HasWalletSpend(wtx.GetHash())) {
|
|
25
|
-
errors.push_back(Untranslated("Transaction has descendants in the wallet"));
|
|
26
|
-
return feebumper::Result::INVALID_PARAMETER;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
if (wallet.chain().hasDescendantsInMempool(wtx.GetHash())) {
|
|
31
|
-
errors.push_back(Untranslated("Transaction has descendants in the mempool"));
|
|
32
|
-
return feebumper::Result::INVALID_PARAMETER;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (wallet.GetTxDepthInMainChain(wtx) != 0) {
|
|
37
|
-
errors.push_back(Untranslated("Transaction has been mined, or is conflicted with a mined transaction"));
|
|
38
|
-
return feebumper::Result::WALLET_ERROR;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (!SignalsOptInRBF(*wtx.tx)) {
|
|
42
|
-
errors.push_back(Untranslated("Transaction is not BIP 125 replaceable"));
|
|
43
|
-
return feebumper::Result::WALLET_ERROR;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (wtx.mapValue.count("replaced_by_txid")) {
|
|
47
|
-
errors.push_back(strprintf(Untranslated("Cannot bump transaction %s which was already bumped by transaction %s"), wtx.GetHash().ToString(), wtx.mapValue.at("replaced_by_txid")));
|
|
48
|
-
return feebumper::Result::WALLET_ERROR;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// check that original tx consists entirely of our inputs
|
|
52
|
-
// if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
|
|
53
|
-
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
|
|
54
|
-
if (!AllInputsMine(wallet, *wtx.tx, filter)) {
|
|
55
|
-
errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
|
|
56
|
-
return feebumper::Result::WALLET_ERROR;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return feebumper::Result::OK;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
//! Check if the user provided a valid feeRate
|
|
64
|
-
static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CFeeRate& newFeerate, const int64_t maxTxSize, std::vector<bilingual_str>& errors)
|
|
65
|
-
{
|
|
66
|
-
// check that fee rate is higher than mempool's minimum fee
|
|
67
|
-
// (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
|
|
68
|
-
// This may occur if the user set fee_rate or paytxfee too low, if fallbackfee is too low, or, perhaps,
|
|
69
|
-
// in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
|
|
70
|
-
// moment earlier. In this case, we report an error to the user, who may adjust the fee.
|
|
71
|
-
CFeeRate minMempoolFeeRate = wallet.chain().mempoolMinFee();
|
|
72
|
-
|
|
73
|
-
if (newFeerate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
|
|
74
|
-
errors.push_back(strprintf(
|
|
75
|
-
Untranslated("New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- "),
|
|
76
|
-
FormatMoney(newFeerate.GetFeePerK()),
|
|
77
|
-
FormatMoney(minMempoolFeeRate.GetFeePerK())));
|
|
78
|
-
return feebumper::Result::WALLET_ERROR;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
CAmount new_total_fee = newFeerate.GetFee(maxTxSize);
|
|
82
|
-
|
|
83
|
-
CFeeRate incrementalRelayFee = std::max(wallet.chain().relayIncrementalFee(), CFeeRate(WALLET_INCREMENTAL_RELAY_FEE));
|
|
84
|
-
|
|
85
|
-
// Given old total fee and transaction size, calculate the old feeRate
|
|
86
|
-
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
|
|
87
|
-
CAmount old_fee = CachedTxGetDebit(wallet, wtx, filter) - wtx.tx->GetValueOut();
|
|
88
|
-
const int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
|
|
89
|
-
CFeeRate nOldFeeRate(old_fee, txSize);
|
|
90
|
-
// Min total fee is old fee + relay fee
|
|
91
|
-
CAmount minTotalFee = nOldFeeRate.GetFee(maxTxSize) + incrementalRelayFee.GetFee(maxTxSize);
|
|
92
|
-
|
|
93
|
-
if (new_total_fee < minTotalFee) {
|
|
94
|
-
errors.push_back(strprintf(Untranslated("Insufficient total fee %s, must be at least %s (oldFee %s + incrementalFee %s)"),
|
|
95
|
-
FormatMoney(new_total_fee), FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxTxSize)), FormatMoney(incrementalRelayFee.GetFee(maxTxSize))));
|
|
96
|
-
return feebumper::Result::INVALID_PARAMETER;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
CAmount requiredFee = GetRequiredFee(wallet, maxTxSize);
|
|
100
|
-
if (new_total_fee < requiredFee) {
|
|
101
|
-
errors.push_back(strprintf(Untranslated("Insufficient total fee (cannot be less than required fee %s)"),
|
|
102
|
-
FormatMoney(requiredFee)));
|
|
103
|
-
return feebumper::Result::INVALID_PARAMETER;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Check that in all cases the new fee doesn't violate maxTxFee
|
|
107
|
-
const CAmount max_tx_fee = wallet.m_default_max_tx_fee;
|
|
108
|
-
if (new_total_fee > max_tx_fee) {
|
|
109
|
-
errors.push_back(strprintf(Untranslated("Specified or calculated fee %s is too high (cannot be higher than -maxtxfee %s)"),
|
|
110
|
-
FormatMoney(new_total_fee), FormatMoney(max_tx_fee)));
|
|
111
|
-
return feebumper::Result::WALLET_ERROR;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return feebumper::Result::OK;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CAmount old_fee, const CCoinControl& coin_control)
|
|
118
|
-
{
|
|
119
|
-
// Get the fee rate of the original transaction. This is calculated from
|
|
120
|
-
// the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the
|
|
121
|
-
// result.
|
|
122
|
-
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
|
|
123
|
-
CFeeRate feerate(old_fee, txSize);
|
|
124
|
-
feerate += CFeeRate(1);
|
|
125
|
-
|
|
126
|
-
// The node has a configurable incremental relay fee. Increment the fee by
|
|
127
|
-
// the minimum of that and the wallet's conservative
|
|
128
|
-
// WALLET_INCREMENTAL_RELAY_FEE value to future proof against changes to
|
|
129
|
-
// network wide policy for incremental relay fee that our node may not be
|
|
130
|
-
// aware of. This ensures we're over the required relay fee rate
|
|
131
|
-
// (BIP 125 rule 4). The replacement tx will be at least as large as the
|
|
132
|
-
// original tx, so the total fee will be greater (BIP 125 rule 3)
|
|
133
|
-
CFeeRate node_incremental_relay_fee = wallet.chain().relayIncrementalFee();
|
|
134
|
-
CFeeRate wallet_incremental_relay_fee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
|
|
135
|
-
feerate += std::max(node_incremental_relay_fee, wallet_incremental_relay_fee);
|
|
136
|
-
|
|
137
|
-
// Fee rate must also be at least the wallet's GetMinimumFeeRate
|
|
138
|
-
CFeeRate min_feerate(GetMinimumFeeRate(wallet, coin_control, /* feeCalc */ nullptr));
|
|
139
|
-
|
|
140
|
-
// Set the required fee rate for the replacement transaction in coin control.
|
|
141
|
-
return std::max(feerate, min_feerate);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
namespace feebumper {
|
|
145
|
-
|
|
146
|
-
bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid)
|
|
147
|
-
{
|
|
148
|
-
LOCK(wallet.cs_wallet);
|
|
149
|
-
const CWalletTx* wtx = wallet.GetWalletTx(txid);
|
|
150
|
-
if (wtx == nullptr) return false;
|
|
151
|
-
|
|
152
|
-
std::vector<bilingual_str> errors_dummy;
|
|
153
|
-
feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy);
|
|
154
|
-
return res == feebumper::Result::OK;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors,
|
|
158
|
-
CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
|
|
159
|
-
{
|
|
160
|
-
// We are going to modify coin control later, copy to re-use
|
|
161
|
-
CCoinControl new_coin_control(coin_control);
|
|
162
|
-
|
|
163
|
-
LOCK(wallet.cs_wallet);
|
|
164
|
-
errors.clear();
|
|
165
|
-
auto it = wallet.mapWallet.find(txid);
|
|
166
|
-
if (it == wallet.mapWallet.end()) {
|
|
167
|
-
errors.push_back(Untranslated("Invalid or non-wallet transaction id"));
|
|
168
|
-
return Result::INVALID_ADDRESS_OR_KEY;
|
|
169
|
-
}
|
|
170
|
-
const CWalletTx& wtx = it->second;
|
|
171
|
-
|
|
172
|
-
Result result = PreconditionChecks(wallet, wtx, errors);
|
|
173
|
-
if (result != Result::OK) {
|
|
174
|
-
return result;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Fill in recipients(and preserve a single change key if there is one)
|
|
178
|
-
std::vector<CRecipient> recipients;
|
|
179
|
-
for (const auto& output : wtx.tx->vout) {
|
|
180
|
-
if (!OutputIsChange(wallet, output)) {
|
|
181
|
-
CRecipient recipient = {output.scriptPubKey, output.nValue, false};
|
|
182
|
-
recipients.push_back(recipient);
|
|
183
|
-
} else {
|
|
184
|
-
CTxDestination change_dest;
|
|
185
|
-
ExtractDestination(output.scriptPubKey, change_dest);
|
|
186
|
-
new_coin_control.destChange = change_dest;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
|
|
191
|
-
old_fee = CachedTxGetDebit(wallet, wtx, filter) - wtx.tx->GetValueOut();
|
|
192
|
-
|
|
193
|
-
if (coin_control.m_feerate) {
|
|
194
|
-
// The user provided a feeRate argument.
|
|
195
|
-
// We calculate this here to avoid compiler warning on the cs_wallet lock
|
|
196
|
-
const int64_t maxTxSize{CalculateMaximumSignedTxSize(*wtx.tx, &wallet).vsize};
|
|
197
|
-
Result res = CheckFeeRate(wallet, wtx, *new_coin_control.m_feerate, maxTxSize, errors);
|
|
198
|
-
if (res != Result::OK) {
|
|
199
|
-
return res;
|
|
200
|
-
}
|
|
201
|
-
} else {
|
|
202
|
-
// The user did not provide a feeRate argument
|
|
203
|
-
new_coin_control.m_feerate = EstimateFeeRate(wallet, wtx, old_fee, new_coin_control);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Fill in required inputs we are double-spending(all of them)
|
|
207
|
-
// N.B.: bip125 doesn't require all the inputs in the replaced transaction to be
|
|
208
|
-
// used in the replacement transaction, but it's very important for wallets to make
|
|
209
|
-
// sure that happens. If not, it would be possible to bump a transaction A twice to
|
|
210
|
-
// A2 and A3 where A2 and A3 don't conflict (or alternatively bump A to A2 and A2
|
|
211
|
-
// to A3 where A and A3 don't conflict). If both later get confirmed then the sender
|
|
212
|
-
// has accidentally double paid.
|
|
213
|
-
for (const auto& inputs : wtx.tx->vin) {
|
|
214
|
-
new_coin_control.Select(COutPoint(inputs.prevout));
|
|
215
|
-
}
|
|
216
|
-
new_coin_control.fAllowOtherInputs = true;
|
|
217
|
-
|
|
218
|
-
// We cannot source new unconfirmed inputs(bip125 rule 2)
|
|
219
|
-
new_coin_control.m_min_depth = 1;
|
|
220
|
-
|
|
221
|
-
CTransactionRef tx_new;
|
|
222
|
-
CAmount fee_ret;
|
|
223
|
-
int change_pos_in_out = -1; // No requested location for change
|
|
224
|
-
bilingual_str fail_reason;
|
|
225
|
-
FeeCalculation fee_calc_out;
|
|
226
|
-
if (!CreateTransaction(wallet, recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, fee_calc_out, false)) {
|
|
227
|
-
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
|
|
228
|
-
return Result::WALLET_ERROR;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Write back new fee if successful
|
|
232
|
-
new_fee = fee_ret;
|
|
233
|
-
|
|
234
|
-
// Write back transaction
|
|
235
|
-
mtx = CMutableTransaction(*tx_new);
|
|
236
|
-
// Mark new tx not replaceable, if requested.
|
|
237
|
-
if (!coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf)) {
|
|
238
|
-
for (auto& input : mtx.vin) {
|
|
239
|
-
if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return Result::OK;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
bool SignTransaction(CWallet& wallet, CMutableTransaction& mtx) {
|
|
247
|
-
LOCK(wallet.cs_wallet);
|
|
248
|
-
return wallet.SignTransaction(mtx);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<bilingual_str>& errors, uint256& bumped_txid)
|
|
252
|
-
{
|
|
253
|
-
LOCK(wallet.cs_wallet);
|
|
254
|
-
if (!errors.empty()) {
|
|
255
|
-
return Result::MISC_ERROR;
|
|
256
|
-
}
|
|
257
|
-
auto it = txid.IsNull() ? wallet.mapWallet.end() : wallet.mapWallet.find(txid);
|
|
258
|
-
if (it == wallet.mapWallet.end()) {
|
|
259
|
-
errors.push_back(Untranslated("Invalid or non-wallet transaction id"));
|
|
260
|
-
return Result::MISC_ERROR;
|
|
261
|
-
}
|
|
262
|
-
const CWalletTx& oldWtx = it->second;
|
|
263
|
-
|
|
264
|
-
// make sure the transaction still has no descendants and hasn't been mined in the meantime
|
|
265
|
-
Result result = PreconditionChecks(wallet, oldWtx, errors);
|
|
266
|
-
if (result != Result::OK) {
|
|
267
|
-
return result;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// commit/broadcast the tx
|
|
271
|
-
CTransactionRef tx = MakeTransactionRef(std::move(mtx));
|
|
272
|
-
mapValue_t mapValue = oldWtx.mapValue;
|
|
273
|
-
mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
|
|
274
|
-
|
|
275
|
-
wallet.CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm);
|
|
276
|
-
|
|
277
|
-
// mark the original tx as bumped
|
|
278
|
-
bumped_txid = tx->GetHash();
|
|
279
|
-
if (!wallet.MarkReplaced(oldWtx.GetHash(), bumped_txid)) {
|
|
280
|
-
errors.push_back(Untranslated("Created new bumpfee transaction but could not mark the original transaction as replaced"));
|
|
281
|
-
}
|
|
282
|
-
return Result::OK;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
} // namespace feebumper
|
|
286
|
-
} // namespace wallet
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2017-2020 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#ifndef BITCOIN_WALLET_FEEBUMPER_H
|
|
6
|
-
#define BITCOIN_WALLET_FEEBUMPER_H
|
|
7
|
-
|
|
8
|
-
#include <primitives/transaction.h>
|
|
9
|
-
|
|
10
|
-
class uint256;
|
|
11
|
-
enum class FeeEstimateMode;
|
|
12
|
-
struct bilingual_str;
|
|
13
|
-
|
|
14
|
-
namespace wallet {
|
|
15
|
-
class CCoinControl;
|
|
16
|
-
class CWallet;
|
|
17
|
-
class CWalletTx;
|
|
18
|
-
|
|
19
|
-
namespace feebumper {
|
|
20
|
-
|
|
21
|
-
enum class Result
|
|
22
|
-
{
|
|
23
|
-
OK,
|
|
24
|
-
INVALID_ADDRESS_OR_KEY,
|
|
25
|
-
INVALID_REQUEST,
|
|
26
|
-
INVALID_PARAMETER,
|
|
27
|
-
WALLET_ERROR,
|
|
28
|
-
MISC_ERROR,
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
//! Return whether transaction can be bumped.
|
|
32
|
-
bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid);
|
|
33
|
-
|
|
34
|
-
//! Create bumpfee transaction based on feerate estimates.
|
|
35
|
-
Result CreateRateBumpTransaction(CWallet& wallet,
|
|
36
|
-
const uint256& txid,
|
|
37
|
-
const CCoinControl& coin_control,
|
|
38
|
-
std::vector<bilingual_str>& errors,
|
|
39
|
-
CAmount& old_fee,
|
|
40
|
-
CAmount& new_fee,
|
|
41
|
-
CMutableTransaction& mtx);
|
|
42
|
-
|
|
43
|
-
//! Sign the new transaction,
|
|
44
|
-
//! @return false if the tx couldn't be found or if it was
|
|
45
|
-
//! impossible to create the signature(s)
|
|
46
|
-
bool SignTransaction(CWallet& wallet, CMutableTransaction& mtx);
|
|
47
|
-
|
|
48
|
-
//! Commit the bumpfee transaction.
|
|
49
|
-
//! @return success in case of CWallet::CommitTransaction was successful,
|
|
50
|
-
//! but sets errors if the tx could not be added to the mempool (will try later)
|
|
51
|
-
//! or if the old transaction could not be marked as replaced.
|
|
52
|
-
Result CommitTransaction(CWallet& wallet,
|
|
53
|
-
const uint256& txid,
|
|
54
|
-
CMutableTransaction&& mtx,
|
|
55
|
-
std::vector<bilingual_str>& errors,
|
|
56
|
-
uint256& bumped_txid);
|
|
57
|
-
|
|
58
|
-
} // namespace feebumper
|
|
59
|
-
} // namespace wallet
|
|
60
|
-
|
|
61
|
-
#endif // BITCOIN_WALLET_FEEBUMPER_H
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
-
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|
3
|
-
// Distributed under the MIT software license, see the accompanying
|
|
4
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
-
|
|
6
|
-
#include <wallet/fees.h>
|
|
7
|
-
|
|
8
|
-
#include <wallet/coincontrol.h>
|
|
9
|
-
#include <wallet/wallet.h>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
namespace wallet {
|
|
13
|
-
CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes)
|
|
14
|
-
{
|
|
15
|
-
return GetRequiredFeeRate(wallet).GetFee(nTxBytes);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc)
|
|
20
|
-
{
|
|
21
|
-
return GetMinimumFeeRate(wallet, coin_control, feeCalc).GetFee(nTxBytes);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
CFeeRate GetRequiredFeeRate(const CWallet& wallet)
|
|
25
|
-
{
|
|
26
|
-
return std::max(wallet.m_min_fee, wallet.chain().relayMinFee());
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, FeeCalculation* feeCalc)
|
|
30
|
-
{
|
|
31
|
-
/* User control of how to calculate fee uses the following parameter precedence:
|
|
32
|
-
1. coin_control.m_feerate
|
|
33
|
-
2. coin_control.m_confirm_target
|
|
34
|
-
3. m_pay_tx_fee (user-set member variable of wallet)
|
|
35
|
-
4. m_confirm_target (user-set member variable of wallet)
|
|
36
|
-
The first parameter that is set is used.
|
|
37
|
-
*/
|
|
38
|
-
CFeeRate feerate_needed;
|
|
39
|
-
if (coin_control.m_feerate) { // 1.
|
|
40
|
-
feerate_needed = *(coin_control.m_feerate);
|
|
41
|
-
if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE;
|
|
42
|
-
// Allow to override automatic min/max check over coin control instance
|
|
43
|
-
if (coin_control.fOverrideFeeRate) return feerate_needed;
|
|
44
|
-
}
|
|
45
|
-
else if (!coin_control.m_confirm_target && wallet.m_pay_tx_fee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for wallet member m_pay_tx_fee
|
|
46
|
-
feerate_needed = wallet.m_pay_tx_fee;
|
|
47
|
-
if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE;
|
|
48
|
-
}
|
|
49
|
-
else { // 2. or 4.
|
|
50
|
-
// We will use smart fee estimation
|
|
51
|
-
unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : wallet.m_confirm_target;
|
|
52
|
-
// By default estimates are economical iff we are signaling opt-in-RBF
|
|
53
|
-
bool conservative_estimate = !coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf);
|
|
54
|
-
// Allow to override the default fee estimate mode over the CoinControl instance
|
|
55
|
-
if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true;
|
|
56
|
-
else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false;
|
|
57
|
-
|
|
58
|
-
feerate_needed = wallet.chain().estimateSmartFee(target, conservative_estimate, feeCalc);
|
|
59
|
-
if (feerate_needed == CFeeRate(0)) {
|
|
60
|
-
// if we don't have enough data for estimateSmartFee, then use fallback fee
|
|
61
|
-
feerate_needed = wallet.m_fallback_fee;
|
|
62
|
-
if (feeCalc) feeCalc->reason = FeeReason::FALLBACK;
|
|
63
|
-
|
|
64
|
-
// directly return if fallback fee is disabled (feerate 0 == disabled)
|
|
65
|
-
if (wallet.m_fallback_fee == CFeeRate(0)) return feerate_needed;
|
|
66
|
-
}
|
|
67
|
-
// Obey mempool min fee when using smart fee estimation
|
|
68
|
-
CFeeRate min_mempool_feerate = wallet.chain().mempoolMinFee();
|
|
69
|
-
if (feerate_needed < min_mempool_feerate) {
|
|
70
|
-
feerate_needed = min_mempool_feerate;
|
|
71
|
-
if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// prevent user from paying a fee below the required fee rate
|
|
76
|
-
CFeeRate required_feerate = GetRequiredFeeRate(wallet);
|
|
77
|
-
if (required_feerate > feerate_needed) {
|
|
78
|
-
feerate_needed = required_feerate;
|
|
79
|
-
if (feeCalc) feeCalc->reason = FeeReason::REQUIRED;
|
|
80
|
-
}
|
|
81
|
-
return feerate_needed;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
CFeeRate GetDiscardRate(const CWallet& wallet)
|
|
85
|
-
{
|
|
86
|
-
unsigned int highest_target = wallet.chain().estimateMaxBlocks();
|
|
87
|
-
CFeeRate discard_rate = wallet.chain().estimateSmartFee(highest_target, false /* conservative */);
|
|
88
|
-
// Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate
|
|
89
|
-
discard_rate = (discard_rate == CFeeRate(0)) ? wallet.m_discard_rate : std::min(discard_rate, wallet.m_discard_rate);
|
|
90
|
-
// Discard rate must be at least dustRelayFee
|
|
91
|
-
discard_rate = std::max(discard_rate, wallet.chain().relayDustFee());
|
|
92
|
-
return discard_rate;
|
|
93
|
-
}
|
|
94
|
-
} // namespace wallet
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
-
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
|
3
|
-
// Distributed under the MIT software license, see the accompanying
|
|
4
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
-
|
|
6
|
-
#ifndef BITCOIN_WALLET_FEES_H
|
|
7
|
-
#define BITCOIN_WALLET_FEES_H
|
|
8
|
-
|
|
9
|
-
#include <consensus/amount.h>
|
|
10
|
-
|
|
11
|
-
class CFeeRate;
|
|
12
|
-
struct FeeCalculation;
|
|
13
|
-
|
|
14
|
-
namespace wallet {
|
|
15
|
-
class CCoinControl;
|
|
16
|
-
class CWallet;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Return the minimum required absolute fee for this size
|
|
20
|
-
* based on the required fee rate
|
|
21
|
-
*/
|
|
22
|
-
CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes);
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Estimate the minimum fee considering user set parameters
|
|
26
|
-
* and the required fee
|
|
27
|
-
*/
|
|
28
|
-
CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc);
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Return the minimum required feerate taking into account the
|
|
32
|
-
* minimum relay feerate and user set minimum transaction feerate
|
|
33
|
-
*/
|
|
34
|
-
CFeeRate GetRequiredFeeRate(const CWallet& wallet);
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Estimate the minimum fee rate considering user set parameters
|
|
38
|
-
* and the required fee
|
|
39
|
-
*/
|
|
40
|
-
CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, FeeCalculation* feeCalc);
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Return the maximum feerate for discarding change.
|
|
44
|
-
*/
|
|
45
|
-
CFeeRate GetDiscardRate(const CWallet& wallet);
|
|
46
|
-
} // namespace wallet
|
|
47
|
-
|
|
48
|
-
#endif // BITCOIN_WALLET_FEES_H
|
|
@@ -52,32 +52,18 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
|
|
|
52
52
|
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
53
53
|
argsman.AddArg("-consolidatefeerate=<amt>", strprintf("The maximum feerate (in %s/kvB) at which transaction building may use more inputs than strictly necessary so that the wallet's UTXO pool can be reduced (default: %s).", CURRENCY_UNIT, FormatMoney(DEFAULT_CONSOLIDATE_FEERATE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
54
54
|
argsman.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
55
|
-
argsman.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kvB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
|
|
56
|
-
"Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target",
|
|
57
|
-
CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
58
|
-
|
|
59
|
-
argsman.AddArg("-fallbackfee=<amt>", strprintf("A fee rate (in %s/kvB) that will be used when fee estimation has insufficient data. 0 to entirely disable the fallbackfee feature. (default: %s)",
|
|
60
|
-
CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
61
55
|
argsman.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u). Warning: Smaller sizes may increase the risk of losing funds when restoring from an old backup, if none of the addresses in the original keypool have been used.", DEFAULT_KEYPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
62
|
-
argsman.AddArg("-
|
|
63
|
-
argsman.AddArg("-maxtxfee=<amt>", strprintf("Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)",
|
|
64
|
-
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
|
65
|
-
argsman.AddArg("-mintxfee=<amt>", strprintf("Fee rates (in %s/kvB) smaller than this are considered zero fee for transaction creation (default: %s)",
|
|
66
|
-
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
67
|
-
argsman.AddArg("-paytxfee=<amt>", strprintf("Fee rate (in %s/kvB) to add to transactions you send (default: %s)",
|
|
68
|
-
CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
56
|
+
argsman.AddArg("-minting", "Mint proof of stake blocks", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
69
57
|
#ifdef ENABLE_EXTERNAL_SIGNER
|
|
70
58
|
argsman.AddArg("-signer=<cmd>", "External signing tool, see doc/external-signer.md", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
71
59
|
#endif
|
|
72
60
|
argsman.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
73
|
-
argsman.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
74
61
|
argsman.AddArg("-wallet=<path>", "Specify wallet path to load at startup. Can be used multiple times to load multiple wallets. Path is to a directory containing wallet data and log files. If the path is not absolute, it is interpreted relative to <walletdir>. This only loads existing wallets and does not create new ones. For backwards compatibility this also accepts names of existing top-level data files in <walletdir>.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
|
|
75
62
|
argsman.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
76
63
|
argsman.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
|
|
77
64
|
#if HAVE_SYSTEM
|
|
78
65
|
argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID, %w is replaced by wallet name, %b is replaced by the hash of the block including the transaction (set to 'unconfirmed' if the transaction is not included) and %h is replaced by the block height (-1 if not included). %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
79
66
|
#endif
|
|
80
|
-
argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
|
81
67
|
|
|
82
68
|
#ifdef USE_BDB
|
|
83
69
|
argsman.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
|
|
@@ -123,6 +109,12 @@ bool WalletInit::ParameterInteraction() const
|
|
|
123
109
|
|
|
124
110
|
if (gArgs.GetBoolArg("-sysperms", false))
|
|
125
111
|
return InitError(Untranslated("-sysperms is not allowed in combination with enabled wallet functionality"));
|
|
112
|
+
if (gArgs.IsArgSet("-reservebalance"))
|
|
113
|
+
{
|
|
114
|
+
std::optional<CAmount> nReserveBalance = ParseMoney(gArgs.GetArg("-reservebalance", ""));
|
|
115
|
+
if (!nReserveBalance)
|
|
116
|
+
return InitError(Untranslated(strprintf("Invalid amount for -reservebalance=<amount>: '%s'", gArgs.GetArg("-reservebalance", ""))));
|
|
117
|
+
}
|
|
126
118
|
|
|
127
119
|
return true;
|
|
128
120
|
}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
#include <consensus/amount.h>
|
|
8
8
|
#include <interfaces/chain.h>
|
|
9
9
|
#include <interfaces/handler.h>
|
|
10
|
-
#include <policy/fees.h>
|
|
11
10
|
#include <primitives/transaction.h>
|
|
12
11
|
#include <rpc/server.h>
|
|
13
12
|
#include <script/standard.h>
|
|
@@ -19,8 +18,6 @@
|
|
|
19
18
|
#include <util/translation.h>
|
|
20
19
|
#include <util/ui_change_type.h>
|
|
21
20
|
#include <wallet/context.h>
|
|
22
|
-
#include <wallet/feebumper.h>
|
|
23
|
-
#include <wallet/fees.h>
|
|
24
21
|
#include <wallet/ismine.h>
|
|
25
22
|
#include <wallet/load.h>
|
|
26
23
|
#include <wallet/receive.h>
|
|
@@ -75,6 +72,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
|
|
|
75
72
|
result.time = wtx.GetTxTime();
|
|
76
73
|
result.value_map = wtx.mapValue;
|
|
77
74
|
result.is_coinbase = wtx.IsCoinBase();
|
|
75
|
+
result.is_coinstake = wtx.IsCoinStake();
|
|
78
76
|
return result;
|
|
79
77
|
}
|
|
80
78
|
|
|
@@ -93,6 +91,7 @@ WalletTxStatus MakeWalletTxStatus(const CWallet& wallet, const CWalletTx& wtx)
|
|
|
93
91
|
result.is_trusted = CachedTxIsTrusted(wallet, wtx);
|
|
94
92
|
result.is_abandoned = wtx.isAbandoned();
|
|
95
93
|
result.is_coinbase = wtx.IsCoinBase();
|
|
94
|
+
result.is_coinstake = wtx.IsCoinStake();
|
|
96
95
|
result.is_in_main_chain = wallet.IsTxInMainChain(wtx);
|
|
97
96
|
return result;
|
|
98
97
|
}
|
|
@@ -247,7 +246,7 @@ public:
|
|
|
247
246
|
{
|
|
248
247
|
LOCK(m_wallet->cs_wallet);
|
|
249
248
|
CTransactionRef tx;
|
|
250
|
-
|
|
249
|
+
CAmount fee_calc_out;
|
|
251
250
|
if (!CreateTransaction(*m_wallet, recipients, tx, fee, change_pos,
|
|
252
251
|
fail_reason, coin_control, fee_calc_out, sign)) {
|
|
253
252
|
return {};
|
|
@@ -267,28 +266,6 @@ public:
|
|
|
267
266
|
LOCK(m_wallet->cs_wallet);
|
|
268
267
|
return m_wallet->AbandonTransaction(txid);
|
|
269
268
|
}
|
|
270
|
-
bool transactionCanBeBumped(const uint256& txid) override
|
|
271
|
-
{
|
|
272
|
-
return feebumper::TransactionCanBeBumped(*m_wallet.get(), txid);
|
|
273
|
-
}
|
|
274
|
-
bool createBumpTransaction(const uint256& txid,
|
|
275
|
-
const CCoinControl& coin_control,
|
|
276
|
-
std::vector<bilingual_str>& errors,
|
|
277
|
-
CAmount& old_fee,
|
|
278
|
-
CAmount& new_fee,
|
|
279
|
-
CMutableTransaction& mtx) override
|
|
280
|
-
{
|
|
281
|
-
return feebumper::CreateRateBumpTransaction(*m_wallet.get(), txid, coin_control, errors, old_fee, new_fee, mtx) == feebumper::Result::OK;
|
|
282
|
-
}
|
|
283
|
-
bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(*m_wallet.get(), mtx); }
|
|
284
|
-
bool commitBumpTransaction(const uint256& txid,
|
|
285
|
-
CMutableTransaction&& mtx,
|
|
286
|
-
std::vector<bilingual_str>& errors,
|
|
287
|
-
uint256& bumped_txid) override
|
|
288
|
-
{
|
|
289
|
-
return feebumper::CommitTransaction(*m_wallet.get(), txid, std::move(mtx), errors, bumped_txid) ==
|
|
290
|
-
feebumper::Result::OK;
|
|
291
|
-
}
|
|
292
269
|
CTransactionRef getTx(const uint256& txid) override
|
|
293
270
|
{
|
|
294
271
|
LOCK(m_wallet->cs_wallet);
|
|
@@ -367,6 +344,7 @@ public:
|
|
|
367
344
|
const auto bal = GetBalance(*m_wallet);
|
|
368
345
|
WalletBalances result;
|
|
369
346
|
result.balance = bal.m_mine_trusted;
|
|
347
|
+
result.stake = bal.m_mine_stake;
|
|
370
348
|
result.unconfirmed_balance = bal.m_mine_untrusted_pending;
|
|
371
349
|
result.immature_balance = bal.m_mine_immature;
|
|
372
350
|
result.have_watch_only = haveWatchOnly();
|
|
@@ -442,7 +420,7 @@ public:
|
|
|
442
420
|
}
|
|
443
421
|
return result;
|
|
444
422
|
}
|
|
445
|
-
|
|
423
|
+
/*
|
|
446
424
|
CAmount getMinimumFee(unsigned int tx_bytes,
|
|
447
425
|
const CCoinControl& coin_control,
|
|
448
426
|
int* returned_target,
|
|
@@ -456,6 +434,7 @@ public:
|
|
|
456
434
|
return result;
|
|
457
435
|
}
|
|
458
436
|
unsigned int getConfirmTarget() override { return m_wallet->m_confirm_target; }
|
|
437
|
+
*/
|
|
459
438
|
bool hdEnabled() override { return m_wallet->IsHDEnabled(); }
|
|
460
439
|
bool canGetAddresses() override { return m_wallet->CanGetAddresses(); }
|
|
461
440
|
bool hasExternalSigner() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER); }
|
|
@@ -466,7 +445,6 @@ public:
|
|
|
466
445
|
return spk_man != nullptr;
|
|
467
446
|
}
|
|
468
447
|
OutputType getDefaultAddressType() override { return m_wallet->m_default_address_type; }
|
|
469
|
-
CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; }
|
|
470
448
|
void remove() override
|
|
471
449
|
{
|
|
472
450
|
RemoveWallet(m_context, m_wallet, false /* load_on_start */);
|
|
@@ -505,6 +483,25 @@ public:
|
|
|
505
483
|
}
|
|
506
484
|
CWallet* wallet() override { return m_wallet.get(); }
|
|
507
485
|
|
|
486
|
+
void relockWalletAfterDuration(int nDuration) override
|
|
487
|
+
{
|
|
488
|
+
// Keep a weak pointer to the wallet so that it is possible to unload the
|
|
489
|
+
// wallet before the following callback is called. If a valid shared pointer
|
|
490
|
+
// is acquired in the callback then the wallet is still loaded.
|
|
491
|
+
std::weak_ptr<CWallet> weak_wallet = m_wallet;
|
|
492
|
+
m_wallet->chain().rpcRunLater(strprintf("lockwallet(%s)", m_wallet->GetName()), [weak_wallet] {
|
|
493
|
+
if (auto shared_wallet = weak_wallet.lock()) {
|
|
494
|
+
LOCK(shared_wallet->cs_wallet);
|
|
495
|
+
shared_wallet->Lock();
|
|
496
|
+
shared_wallet->nRelockTime = 0;
|
|
497
|
+
}
|
|
498
|
+
}, nDuration);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
virtual std::shared_ptr<CWallet> getWallet() override {
|
|
502
|
+
return m_wallet;
|
|
503
|
+
}
|
|
504
|
+
|
|
508
505
|
WalletContext& m_context;
|
|
509
506
|
std::shared_ptr<CWallet> m_wallet;
|
|
510
507
|
};
|
|
@@ -231,6 +231,36 @@ void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
|
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
LOCK(wallet.cs_wallet);
|
|
234
|
+
|
|
235
|
+
// treat coinstake as a single "recieve" entry
|
|
236
|
+
if (wtx.IsCoinStake())
|
|
237
|
+
{
|
|
238
|
+
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i)
|
|
239
|
+
{
|
|
240
|
+
const CTxOut& txout = wtx.tx->vout[i];
|
|
241
|
+
isminetype fIsMine = wallet.IsMine(txout);
|
|
242
|
+
|
|
243
|
+
// get my vout with positive output
|
|
244
|
+
if (!(fIsMine & filter) || txout.nValue <= 0)
|
|
245
|
+
continue;
|
|
246
|
+
|
|
247
|
+
// get address
|
|
248
|
+
CTxDestination address = CNoDestination();
|
|
249
|
+
ExtractDestination(txout.scriptPubKey, address);
|
|
250
|
+
|
|
251
|
+
// nfee is negative for coinstake generation, because we are gaining money from it
|
|
252
|
+
COutputEntry output = {address, -nFee, (int)i};
|
|
253
|
+
listReceived.push_back(output);
|
|
254
|
+
nFee = 0;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// if we reach here there is probably a mistake
|
|
259
|
+
COutputEntry output = {CNoDestination(), 0, 0};
|
|
260
|
+
listReceived.push_back(output);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
234
264
|
// Sent/received.
|
|
235
265
|
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i)
|
|
236
266
|
{
|
|
@@ -53,6 +53,7 @@ struct Balance {
|
|
|
53
53
|
CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
|
|
54
54
|
CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
|
|
55
55
|
CAmount m_mine_immature{0}; //!< Immature coinbases in the main chain
|
|
56
|
+
CAmount m_mine_stake{0}; //!< Staked, non-spendable until maturity
|
|
56
57
|
CAmount m_watchonly_trusted{0};
|
|
57
58
|
CAmount m_watchonly_untrusted_pending{0};
|
|
58
59
|
CAmount m_watchonly_immature{0};
|
|
@@ -17,7 +17,7 @@ namespace wallet {
|
|
|
17
17
|
RPCHelpMan getnewaddress()
|
|
18
18
|
{
|
|
19
19
|
return RPCHelpMan{"getnewaddress",
|
|
20
|
-
"\nReturns a new
|
|
20
|
+
"\nReturns a new Peercoin address for receiving payments.\n"
|
|
21
21
|
"If 'label' is specified, it is added to the address book \n"
|
|
22
22
|
"so payments received with the address will be associated with 'label'.\n",
|
|
23
23
|
{
|
|
@@ -25,7 +25,7 @@ RPCHelpMan getnewaddress()
|
|
|
25
25
|
{"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
|
|
26
26
|
},
|
|
27
27
|
RPCResult{
|
|
28
|
-
RPCResult::Type::STR, "address", "The new
|
|
28
|
+
RPCResult::Type::STR, "address", "The new peercoin address"
|
|
29
29
|
},
|
|
30
30
|
RPCExamples{
|
|
31
31
|
HelpExampleCli("getnewaddress", "")
|
|
@@ -72,7 +72,7 @@ RPCHelpMan getnewaddress()
|
|
|
72
72
|
RPCHelpMan getrawchangeaddress()
|
|
73
73
|
{
|
|
74
74
|
return RPCHelpMan{"getrawchangeaddress",
|
|
75
|
-
"\nReturns a new
|
|
75
|
+
"\nReturns a new Peercoin address, for receiving change.\n"
|
|
76
76
|
"This is for use with raw transactions, NOT normal use.\n",
|
|
77
77
|
{
|
|
78
78
|
{"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
|
|
@@ -122,7 +122,7 @@ RPCHelpMan setlabel()
|
|
|
122
122
|
return RPCHelpMan{"setlabel",
|
|
123
123
|
"\nSets the label associated with the given address.\n",
|
|
124
124
|
{
|
|
125
|
-
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The
|
|
125
|
+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The peercoin address to be associated with a label."},
|
|
126
126
|
{"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
|
|
127
127
|
},
|
|
128
128
|
RPCResult{RPCResult::Type::NONE, "", ""},
|
|
@@ -139,7 +139,7 @@ RPCHelpMan setlabel()
|
|
|
139
139
|
|
|
140
140
|
CTxDestination dest = DecodeDestination(request.params[0].get_str());
|
|
141
141
|
if (!IsValidDestination(dest)) {
|
|
142
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid
|
|
142
|
+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Peercoin address");
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
std::string label = LabelFromValue(request.params[1]);
|
|
@@ -169,7 +169,7 @@ RPCHelpMan listaddressgroupings()
|
|
|
169
169
|
{
|
|
170
170
|
{RPCResult::Type::ARR_FIXED, "", "",
|
|
171
171
|
{
|
|
172
|
-
{RPCResult::Type::STR, "address", "The
|
|
172
|
+
{RPCResult::Type::STR, "address", "The peercoin address"},
|
|
173
173
|
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
|
|
174
174
|
{RPCResult::Type::STR, "label", /*optional=*/true, "The label"},
|
|
175
175
|
}},
|
|
@@ -219,15 +219,15 @@ RPCHelpMan addmultisigaddress()
|
|
|
219
219
|
{
|
|
220
220
|
return RPCHelpMan{"addmultisigaddress",
|
|
221
221
|
"\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
|
|
222
|
-
"Each key is a
|
|
222
|
+
"Each key is a Peercoin address or hex-encoded public key.\n"
|
|
223
223
|
"This functionality is only intended for use with non-watchonly addresses.\n"
|
|
224
224
|
"See `importaddress` for watchonly p2sh address support.\n"
|
|
225
225
|
"If 'label' is specified, assign address to that label.\n",
|
|
226
226
|
{
|
|
227
227
|
{"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys or addresses."},
|
|
228
|
-
{"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The
|
|
228
|
+
{"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The peercoin addresses or hex-encoded public keys",
|
|
229
229
|
{
|
|
230
|
-
{"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "
|
|
230
|
+
{"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "peercoin address or hex-encoded public key"},
|
|
231
231
|
},
|
|
232
232
|
},
|
|
233
233
|
{"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A label to assign the addresses to."},
|
|
@@ -496,15 +496,15 @@ static UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestinatio
|
|
|
496
496
|
RPCHelpMan getaddressinfo()
|
|
497
497
|
{
|
|
498
498
|
return RPCHelpMan{"getaddressinfo",
|
|
499
|
-
"\nReturn information about the given
|
|
499
|
+
"\nReturn information about the given peercoin address.\n"
|
|
500
500
|
"Some of the information will only be present if the address is in the active wallet.\n",
|
|
501
501
|
{
|
|
502
|
-
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The
|
|
502
|
+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The peercoin address for which to get information."},
|
|
503
503
|
},
|
|
504
504
|
RPCResult{
|
|
505
505
|
RPCResult::Type::OBJ, "", "",
|
|
506
506
|
{
|
|
507
|
-
{RPCResult::Type::STR, "address", "The
|
|
507
|
+
{RPCResult::Type::STR, "address", "The peercoin address validated."},
|
|
508
508
|
{RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address."},
|
|
509
509
|
{RPCResult::Type::BOOL, "ismine", "If the address is yours."},
|
|
510
510
|
{RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."},
|
|
@@ -768,7 +768,7 @@ RPCHelpMan walletdisplayaddress()
|
|
|
768
768
|
"walletdisplayaddress",
|
|
769
769
|
"Display address on an external signer for verification.",
|
|
770
770
|
{
|
|
771
|
-
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "
|
|
771
|
+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "peercoin address to display"},
|
|
772
772
|
},
|
|
773
773
|
RPCResult{
|
|
774
774
|
RPCResult::Type::OBJ,"","",
|
|
@@ -148,13 +148,6 @@ RPCHelpMan importprivkey()
|
|
|
148
148
|
if (!request.params[2].isNull())
|
|
149
149
|
fRescan = request.params[2].get_bool();
|
|
150
150
|
|
|
151
|
-
if (fRescan && pwallet->chain().havePruned()) {
|
|
152
|
-
// Exit early and print an error.
|
|
153
|
-
// If a block is pruned after this check, we will import the key(s),
|
|
154
|
-
// but fail the rescan with a generic error.
|
|
155
|
-
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
|
|
156
|
-
}
|
|
157
|
-
|
|
158
151
|
if (fRescan && !reserver.reserve()) {
|
|
159
152
|
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
|
|
160
153
|
}
|
|
@@ -239,13 +232,6 @@ RPCHelpMan importaddress()
|
|
|
239
232
|
if (!request.params[2].isNull())
|
|
240
233
|
fRescan = request.params[2].get_bool();
|
|
241
234
|
|
|
242
|
-
if (fRescan && pwallet->chain().havePruned()) {
|
|
243
|
-
// Exit early and print an error.
|
|
244
|
-
// If a block is pruned after this check, we will import the key(s),
|
|
245
|
-
// but fail the rescan with a generic error.
|
|
246
|
-
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
|
|
247
|
-
}
|
|
248
|
-
|
|
249
235
|
WalletRescanReserver reserver(*pwallet);
|
|
250
236
|
if (fRescan && !reserver.reserve()) {
|
|
251
237
|
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
|
|
@@ -284,7 +270,7 @@ RPCHelpMan importaddress()
|
|
|
284
270
|
|
|
285
271
|
pwallet->ImportScriptPubKeys(strLabel, scripts, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
|
|
286
272
|
} else {
|
|
287
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid
|
|
273
|
+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Peercoin address or script");
|
|
288
274
|
}
|
|
289
275
|
}
|
|
290
276
|
if (fRescan)
|
|
@@ -301,99 +287,6 @@ RPCHelpMan importaddress()
|
|
|
301
287
|
};
|
|
302
288
|
}
|
|
303
289
|
|
|
304
|
-
RPCHelpMan importprunedfunds()
|
|
305
|
-
{
|
|
306
|
-
return RPCHelpMan{"importprunedfunds",
|
|
307
|
-
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
|
|
308
|
-
{
|
|
309
|
-
{"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
|
|
310
|
-
{"txoutproof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex output from gettxoutproof that contains the transaction"},
|
|
311
|
-
},
|
|
312
|
-
RPCResult{RPCResult::Type::NONE, "", ""},
|
|
313
|
-
RPCExamples{""},
|
|
314
|
-
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
315
|
-
{
|
|
316
|
-
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
|
317
|
-
if (!pwallet) return NullUniValue;
|
|
318
|
-
|
|
319
|
-
CMutableTransaction tx;
|
|
320
|
-
if (!DecodeHexTx(tx, request.params[0].get_str())) {
|
|
321
|
-
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
|
|
322
|
-
}
|
|
323
|
-
uint256 hashTx = tx.GetHash();
|
|
324
|
-
|
|
325
|
-
CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
|
|
326
|
-
CMerkleBlock merkleBlock;
|
|
327
|
-
ssMB >> merkleBlock;
|
|
328
|
-
|
|
329
|
-
//Search partial merkle tree in proof for our transaction and index in valid block
|
|
330
|
-
std::vector<uint256> vMatch;
|
|
331
|
-
std::vector<unsigned int> vIndex;
|
|
332
|
-
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) {
|
|
333
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
LOCK(pwallet->cs_wallet);
|
|
337
|
-
int height;
|
|
338
|
-
if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(), FoundBlock().height(height))) {
|
|
339
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
std::vector<uint256>::const_iterator it;
|
|
343
|
-
if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx)) == vMatch.end()) {
|
|
344
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
unsigned int txnIndex = vIndex[it - vMatch.begin()];
|
|
348
|
-
|
|
349
|
-
CTransactionRef tx_ref = MakeTransactionRef(tx);
|
|
350
|
-
if (pwallet->IsMine(*tx_ref)) {
|
|
351
|
-
pwallet->AddToWallet(std::move(tx_ref), TxStateConfirmed{merkleBlock.header.GetHash(), height, static_cast<int>(txnIndex)});
|
|
352
|
-
return NullUniValue;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
|
|
356
|
-
},
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
RPCHelpMan removeprunedfunds()
|
|
361
|
-
{
|
|
362
|
-
return RPCHelpMan{"removeprunedfunds",
|
|
363
|
-
"\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
|
|
364
|
-
{
|
|
365
|
-
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},
|
|
366
|
-
},
|
|
367
|
-
RPCResult{RPCResult::Type::NONE, "", ""},
|
|
368
|
-
RPCExamples{
|
|
369
|
-
HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
|
|
370
|
-
"\nAs a JSON-RPC call\n"
|
|
371
|
-
+ HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
|
|
372
|
-
},
|
|
373
|
-
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
374
|
-
{
|
|
375
|
-
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
|
376
|
-
if (!pwallet) return NullUniValue;
|
|
377
|
-
|
|
378
|
-
LOCK(pwallet->cs_wallet);
|
|
379
|
-
|
|
380
|
-
uint256 hash(ParseHashV(request.params[0], "txid"));
|
|
381
|
-
std::vector<uint256> vHash;
|
|
382
|
-
vHash.push_back(hash);
|
|
383
|
-
std::vector<uint256> vHashOut;
|
|
384
|
-
|
|
385
|
-
if (pwallet->ZapSelectTx(vHash, vHashOut) != DBErrors::LOAD_OK) {
|
|
386
|
-
throw JSONRPCError(RPC_WALLET_ERROR, "Could not properly delete the transaction.");
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
if(vHashOut.empty()) {
|
|
390
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction does not exist in wallet.");
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return NullUniValue;
|
|
394
|
-
},
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
290
|
|
|
398
291
|
RPCHelpMan importpubkey()
|
|
399
292
|
{
|
|
@@ -433,13 +326,6 @@ RPCHelpMan importpubkey()
|
|
|
433
326
|
if (!request.params[2].isNull())
|
|
434
327
|
fRescan = request.params[2].get_bool();
|
|
435
328
|
|
|
436
|
-
if (fRescan && pwallet->chain().havePruned()) {
|
|
437
|
-
// Exit early and print an error.
|
|
438
|
-
// If a block is pruned after this check, we will import the key(s),
|
|
439
|
-
// but fail the rescan with a generic error.
|
|
440
|
-
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
|
|
441
|
-
}
|
|
442
|
-
|
|
443
329
|
WalletRescanReserver reserver(*pwallet);
|
|
444
330
|
if (fRescan && !reserver.reserve()) {
|
|
445
331
|
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
|
|
@@ -505,13 +391,6 @@ RPCHelpMan importwallet()
|
|
|
505
391
|
|
|
506
392
|
EnsureLegacyScriptPubKeyMan(*pwallet, true);
|
|
507
393
|
|
|
508
|
-
if (pwallet->chain().havePruned()) {
|
|
509
|
-
// Exit early and print an error.
|
|
510
|
-
// If a block is pruned after this check, we will import the key(s),
|
|
511
|
-
// but fail the rescan with a generic error.
|
|
512
|
-
throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when blocks are pruned");
|
|
513
|
-
}
|
|
514
|
-
|
|
515
394
|
WalletRescanReserver reserver(*pwallet);
|
|
516
395
|
if (!reserver.reserve()) {
|
|
517
396
|
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
|
|
@@ -644,7 +523,7 @@ RPCHelpMan dumpprivkey()
|
|
|
644
523
|
"\nReveals the private key corresponding to 'address'.\n"
|
|
645
524
|
"Then the importprivkey can be used with this output\n",
|
|
646
525
|
{
|
|
647
|
-
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The
|
|
526
|
+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The peercoin address for the private key"},
|
|
648
527
|
},
|
|
649
528
|
RPCResult{
|
|
650
529
|
RPCResult::Type::STR, "key", "The private key"
|
|
@@ -668,7 +547,7 @@ RPCHelpMan dumpprivkey()
|
|
|
668
547
|
std::string strAddress = request.params[0].get_str();
|
|
669
548
|
CTxDestination dest = DecodeDestination(strAddress);
|
|
670
549
|
if (!IsValidDestination(dest)) {
|
|
671
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid
|
|
550
|
+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Peercoin address");
|
|
672
551
|
}
|
|
673
552
|
auto keyid = GetKeyForDestination(spk_man, dest);
|
|
674
553
|
if (keyid.IsNull()) {
|
|
@@ -1420,7 +1299,7 @@ RPCHelpMan importmulti()
|
|
|
1420
1299
|
"block from time %d, which is after or within %d seconds of key creation, and "
|
|
1421
1300
|
"could contain transactions pertaining to the key. As a result, transactions "
|
|
1422
1301
|
"and coins using this key may not appear in the wallet. This error could be "
|
|
1423
|
-
"caused by pruning or data corruption (see
|
|
1302
|
+
"caused by pruning or data corruption (see peercoind log for details) and could "
|
|
1424
1303
|
"be dealt with by downloading and rescanning the relevant blocks (see -reindex "
|
|
1425
1304
|
"option and rescanblockchain RPC).",
|
|
1426
1305
|
GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
|
|
@@ -28,7 +28,7 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
|
|
|
28
28
|
// Get the address
|
|
29
29
|
CTxDestination dest = DecodeDestination(params[0].get_str());
|
|
30
30
|
if (!IsValidDestination(dest)) {
|
|
31
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid
|
|
31
|
+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Peercoin address");
|
|
32
32
|
}
|
|
33
33
|
CScript script_pub_key = GetScriptForDestination(dest);
|
|
34
34
|
if (!wallet.IsMine(script_pub_key)) {
|
|
@@ -82,7 +82,7 @@ RPCHelpMan getreceivedbyaddress()
|
|
|
82
82
|
return RPCHelpMan{"getreceivedbyaddress",
|
|
83
83
|
"\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
|
|
84
84
|
{
|
|
85
|
-
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The
|
|
85
|
+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The peercoin address for transactions."},
|
|
86
86
|
{"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "Only include transactions confirmed at least this many times."},
|
|
87
87
|
{"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
|
|
88
88
|
},
|
|
@@ -243,7 +243,7 @@ RPCHelpMan lockunspent()
|
|
|
243
243
|
"\nUpdates list of temporarily unspendable outputs.\n"
|
|
244
244
|
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
|
|
245
245
|
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
|
|
246
|
-
"A locked transaction output will not be chosen by automatic coin selection, when spending
|
|
246
|
+
"A locked transaction output will not be chosen by automatic coin selection, when spending peercoins.\n"
|
|
247
247
|
"Manually selected coins are automatically unlocked.\n"
|
|
248
248
|
"Locks are stored in memory only, unless persistent=true, in which case they will be written to the\n"
|
|
249
249
|
"wallet database and loaded on node start. Unwritten (persistent=false) locks are always cleared\n"
|
|
@@ -507,9 +507,9 @@ RPCHelpMan listunspent()
|
|
|
507
507
|
{
|
|
508
508
|
{"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum confirmations to filter"},
|
|
509
509
|
{"maxconf", RPCArg::Type::NUM, RPCArg::Default{9999999}, "The maximum confirmations to filter"},
|
|
510
|
-
{"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The
|
|
510
|
+
{"addresses", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The peercoin addresses to filter",
|
|
511
511
|
{
|
|
512
|
-
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "
|
|
512
|
+
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "peercoin address"},
|
|
513
513
|
},
|
|
514
514
|
},
|
|
515
515
|
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include outputs that are not safe to spend\n"
|
|
@@ -530,7 +530,7 @@ RPCHelpMan listunspent()
|
|
|
530
530
|
{
|
|
531
531
|
{RPCResult::Type::STR_HEX, "txid", "the transaction id"},
|
|
532
532
|
{RPCResult::Type::NUM, "vout", "the vout value"},
|
|
533
|
-
{RPCResult::Type::STR, "address", /*optional=*/true, "the
|
|
533
|
+
{RPCResult::Type::STR, "address", /*optional=*/true, "the peercoin address"},
|
|
534
534
|
{RPCResult::Type::STR, "label", /*optional=*/true, "The associated label, or \"\" for the default label"},
|
|
535
535
|
{RPCResult::Type::STR, "scriptPubKey", "the script key"},
|
|
536
536
|
{RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
|
|
@@ -582,7 +582,7 @@ RPCHelpMan listunspent()
|
|
|
582
582
|
const UniValue& input = inputs[idx];
|
|
583
583
|
CTxDestination dest = DecodeDestination(input.get_str());
|
|
584
584
|
if (!IsValidDestination(dest)) {
|
|
585
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid
|
|
585
|
+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Peercoin address: ") + input.get_str());
|
|
586
586
|
}
|
|
587
587
|
if (!destinations.insert(dest).second) {
|
|
588
588
|
throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str());
|
|
@@ -19,15 +19,19 @@ RPCHelpMan walletpassphrase()
|
|
|
19
19
|
{
|
|
20
20
|
{"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet passphrase"},
|
|
21
21
|
{"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The time to keep the decryption key in seconds; capped at 100000000 (~3 years)."},
|
|
22
|
+
{"mintonly", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Unlock for minting only"},
|
|
22
23
|
},
|
|
23
|
-
RPCResult{RPCResult::Type::
|
|
24
|
+
RPCResult{RPCResult::Type::OBJ, "", "",
|
|
25
|
+
{
|
|
26
|
+
{RPCResult::Type::BOOL, "unlocked_minting_only", "Whether wallet is unlocked for minting only."}
|
|
27
|
+
}},
|
|
24
28
|
RPCExamples{
|
|
25
29
|
"\nUnlock the wallet for 60 seconds\n"
|
|
26
|
-
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
|
|
30
|
+
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60, false") +
|
|
27
31
|
"\nLock the wallet again (before 60 seconds)\n"
|
|
28
32
|
+ HelpExampleCli("walletlock", "") +
|
|
29
33
|
"\nAs a JSON-RPC call\n"
|
|
30
|
-
+ HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
|
|
34
|
+
+ HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60, false")
|
|
31
35
|
},
|
|
32
36
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
33
37
|
{
|
|
@@ -98,7 +102,16 @@ RPCHelpMan walletpassphrase()
|
|
|
98
102
|
}
|
|
99
103
|
}, nSleepTime);
|
|
100
104
|
|
|
101
|
-
|
|
105
|
+
// peercoin: if user OS account compromised prevent trivial sendmoney commands
|
|
106
|
+
if (request.params.size() > 2)
|
|
107
|
+
fWalletUnlockMintOnly = request.params[2].get_bool();
|
|
108
|
+
else
|
|
109
|
+
fWalletUnlockMintOnly = false;
|
|
110
|
+
|
|
111
|
+
UniValue ret(UniValue::VOBJ);
|
|
112
|
+
ret.pushKV("unlocked_minting_only", fWalletUnlockMintOnly);
|
|
113
|
+
|
|
114
|
+
return ret;
|
|
102
115
|
},
|
|
103
116
|
};
|
|
104
117
|
}
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
#include <util/translation.h>
|
|
13
13
|
#include <util/vector.h>
|
|
14
14
|
#include <wallet/coincontrol.h>
|
|
15
|
-
#include <wallet/feebumper.h>
|
|
16
15
|
#include <wallet/rpc/util.h>
|
|
17
16
|
#include <wallet/spend.h>
|
|
18
17
|
#include <wallet/wallet.h>
|
|
@@ -27,7 +26,7 @@ static void ParseRecipients(const UniValue& address_amounts, const UniValue& sub
|
|
|
27
26
|
for (const std::string& address: address_amounts.getKeys()) {
|
|
28
27
|
CTxDestination dest = DecodeDestination(address);
|
|
29
28
|
if (!IsValidDestination(dest)) {
|
|
30
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid
|
|
29
|
+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Peercoin address: ") + address);
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
if (destinations.count(dest)) {
|
|
@@ -69,7 +68,7 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto
|
|
|
69
68
|
int nChangePosRet = -1;
|
|
70
69
|
bilingual_str error;
|
|
71
70
|
CTransactionRef tx;
|
|
72
|
-
|
|
71
|
+
CAmount fee_calc_out;
|
|
73
72
|
const bool fCreated = CreateTransaction(wallet, recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
|
|
74
73
|
if (!fCreated) {
|
|
75
74
|
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
|
|
@@ -78,57 +77,18 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto
|
|
|
78
77
|
if (verbose) {
|
|
79
78
|
UniValue entry(UniValue::VOBJ);
|
|
80
79
|
entry.pushKV("txid", tx->GetHash().GetHex());
|
|
81
|
-
entry.pushKV("fee_reason", StringForFeeReason(fee_calc_out.reason));
|
|
82
80
|
return entry;
|
|
83
81
|
}
|
|
84
82
|
return tx->GetHash().GetHex();
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Update coin control with fee estimation based on the given parameters
|
|
90
|
-
*
|
|
91
|
-
* @param[in] wallet Wallet reference
|
|
92
|
-
* @param[in,out] cc Coin control to be updated
|
|
93
|
-
* @param[in] conf_target UniValue integer; confirmation target in blocks, values between 1 and 1008 are valid per policy/fees.h;
|
|
94
|
-
* @param[in] estimate_mode UniValue string; fee estimation mode, valid values are "unset", "economical" or "conservative";
|
|
95
|
-
* @param[in] fee_rate UniValue real; fee rate in sat/vB;
|
|
96
|
-
* if present, both conf_target and estimate_mode must either be null, or "unset"
|
|
97
|
-
* @param[in] override_min_fee bool; whether to set fOverrideFeeRate to true to disable minimum fee rate checks and instead
|
|
98
|
-
* verify only that fee_rate is greater than 0
|
|
99
|
-
* @throws a JSONRPCError if conf_target, estimate_mode, or fee_rate contain invalid values or are in conflict
|
|
100
|
-
*/
|
|
101
|
-
static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, bool override_min_fee)
|
|
102
|
-
{
|
|
103
|
-
if (!fee_rate.isNull()) {
|
|
104
|
-
if (!conf_target.isNull()) {
|
|
105
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
|
|
106
|
-
}
|
|
107
|
-
if (!estimate_mode.isNull() && estimate_mode.get_str() != "unset") {
|
|
108
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and fee_rate");
|
|
109
|
-
}
|
|
110
|
-
// Fee rates in sat/vB cannot represent more than 3 significant digits.
|
|
111
|
-
cc.m_feerate = CFeeRate{AmountFromValue(fee_rate, /* decimals */ 3)};
|
|
112
|
-
if (override_min_fee) cc.fOverrideFeeRate = true;
|
|
113
|
-
// Default RBF to true for explicit fee_rate, if unset.
|
|
114
|
-
if (!cc.m_signal_bip125_rbf) cc.m_signal_bip125_rbf = true;
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
if (!estimate_mode.isNull() && !FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) {
|
|
118
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());
|
|
119
|
-
}
|
|
120
|
-
if (!conf_target.isNull()) {
|
|
121
|
-
cc.m_confirm_target = ParseConfirmTarget(conf_target, wallet.chain().estimateMaxBlocks());
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
85
|
RPCHelpMan sendtoaddress()
|
|
126
86
|
{
|
|
127
87
|
return RPCHelpMan{"sendtoaddress",
|
|
128
88
|
"\nSend an amount to a given address." +
|
|
129
89
|
HELP_REQUIRING_PASSPHRASE,
|
|
130
90
|
{
|
|
131
|
-
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The
|
|
91
|
+
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The peercoin address to send to."},
|
|
132
92
|
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
|
|
133
93
|
{"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment used to store what the transaction is for.\n"
|
|
134
94
|
"This is not part of the transaction, just kept in your wallet."},
|
|
@@ -136,11 +96,8 @@ RPCHelpMan sendtoaddress()
|
|
|
136
96
|
"to which you're sending the transaction. This is not part of the \n"
|
|
137
97
|
"transaction, just kept in your wallet."},
|
|
138
98
|
{"subtractfeefromamount", RPCArg::Type::BOOL, RPCArg::Default{false}, "The fee will be deducted from the amount being sent.\n"
|
|
139
|
-
"The recipient will receive less
|
|
140
|
-
{"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
|
|
99
|
+
"The recipient will receive less peercoins than you enter in the amount field."},
|
|
141
100
|
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
|
|
142
|
-
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
|
|
143
|
-
" \"" + FeeModes("\"\n\"") + "\""},
|
|
144
101
|
{"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{true}, "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n"
|
|
145
102
|
"dirty if they have previously been used in a transaction. If true, this also activates avoidpartialspends, grouping outputs by their addresses."},
|
|
146
103
|
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
|
|
@@ -163,13 +120,10 @@ RPCHelpMan sendtoaddress()
|
|
|
163
120
|
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1") +
|
|
164
121
|
"\nSend 0.1 BTC with a confirmation target of 6 blocks in economical fee estimate mode using positional arguments\n"
|
|
165
122
|
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"donation\" \"sean's outpost\" false true 6 economical") +
|
|
166
|
-
"\nSend 0.1 BTC with a fee rate of 1.1 " + CURRENCY_ATOM + "/vB, subtract fee from amount, BIP125-replaceable, using positional arguments\n"
|
|
167
|
-
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"drinks\" \"room77\" true true null \"unset\" null 1.1") +
|
|
168
123
|
"\nSend 0.2 BTC with a confirmation target of 6 blocks in economical fee estimate mode using named arguments\n"
|
|
169
124
|
+ HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.2 conf_target=6 estimate_mode=\"economical\"") +
|
|
170
125
|
"\nSend 0.5 BTC with a fee rate of 25 " + CURRENCY_ATOM + "/vB using named arguments\n"
|
|
171
126
|
+ HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.5 fee_rate=25")
|
|
172
|
-
+ HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.5 fee_rate=25 subtractfeefromamount=false replaceable=true avoid_reuse=true comment=\"2 pizzas\" comment_to=\"jeremy\" verbose=true")
|
|
173
127
|
},
|
|
174
128
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
175
129
|
{
|
|
@@ -195,16 +149,11 @@ RPCHelpMan sendtoaddress()
|
|
|
195
149
|
}
|
|
196
150
|
|
|
197
151
|
CCoinControl coin_control;
|
|
198
|
-
if (!request.params[5].isNull()) {
|
|
199
|
-
coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
|
|
200
|
-
}
|
|
201
152
|
|
|
202
153
|
coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(*pwallet, request.params[8]);
|
|
203
154
|
// We also enable partial spend avoidance if reuse avoidance is set.
|
|
204
155
|
coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse;
|
|
205
156
|
|
|
206
|
-
SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[9], /* override_min_fee */ false);
|
|
207
|
-
|
|
208
157
|
EnsureWalletIsUnlocked(*pwallet);
|
|
209
158
|
|
|
210
159
|
UniValue address_amounts(UniValue::VOBJ);
|
|
@@ -233,23 +182,20 @@ RPCHelpMan sendmany()
|
|
|
233
182
|
{"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", "\"\""},
|
|
234
183
|
{"amounts", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::NO, "The addresses and amounts",
|
|
235
184
|
{
|
|
236
|
-
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The
|
|
185
|
+
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The peercoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
|
|
237
186
|
},
|
|
238
187
|
},
|
|
239
188
|
{"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
|
|
240
189
|
{"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
|
|
241
190
|
{"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The addresses.\n"
|
|
242
191
|
"The fee will be equally deducted from the amount of each selected address.\n"
|
|
243
|
-
"Those recipients will receive less
|
|
192
|
+
"Those recipients will receive less peercoins than you enter in their corresponding amount field.\n"
|
|
244
193
|
"If no addresses are specified here, the sender pays the fee.",
|
|
245
194
|
{
|
|
246
195
|
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
|
|
247
196
|
},
|
|
248
197
|
},
|
|
249
|
-
{"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
|
|
250
198
|
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
|
|
251
|
-
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
|
|
252
|
-
" \"" + FeeModes("\"\n\"") + "\""},
|
|
253
199
|
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
|
|
254
200
|
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, return extra infomration about the transaction."},
|
|
255
201
|
},
|
|
@@ -302,11 +248,6 @@ RPCHelpMan sendmany()
|
|
|
302
248
|
subtractFeeFromAmount = request.params[4].get_array();
|
|
303
249
|
|
|
304
250
|
CCoinControl coin_control;
|
|
305
|
-
if (!request.params[5].isNull()) {
|
|
306
|
-
coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[8], /* override_min_fee */ false);
|
|
310
251
|
|
|
311
252
|
std::vector<CRecipient> recipients;
|
|
312
253
|
ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
|
|
@@ -339,20 +280,6 @@ RPCHelpMan settxfee()
|
|
|
339
280
|
|
|
340
281
|
LOCK(pwallet->cs_wallet);
|
|
341
282
|
|
|
342
|
-
CAmount nAmount = AmountFromValue(request.params[0]);
|
|
343
|
-
CFeeRate tx_fee_rate(nAmount, 1000);
|
|
344
|
-
CFeeRate max_tx_fee_rate(pwallet->m_default_max_tx_fee, 1000);
|
|
345
|
-
if (tx_fee_rate == CFeeRate(0)) {
|
|
346
|
-
// automatic selection
|
|
347
|
-
} else if (tx_fee_rate < pwallet->chain().relayMinFee()) {
|
|
348
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than min relay tx fee (%s)", pwallet->chain().relayMinFee().ToString()));
|
|
349
|
-
} else if (tx_fee_rate < pwallet->m_min_fee) {
|
|
350
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than wallet min fee (%s)", pwallet->m_min_fee.ToString()));
|
|
351
|
-
} else if (tx_fee_rate > max_tx_fee_rate) {
|
|
352
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be more than wallet max tx fee (%s)", max_tx_fee_rate.ToString()));
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
pwallet->m_pay_tx_fee = tx_fee_rate;
|
|
356
283
|
return true;
|
|
357
284
|
},
|
|
358
285
|
};
|
|
@@ -364,10 +291,6 @@ static std::vector<RPCArg> FundTxDoc()
|
|
|
364
291
|
{
|
|
365
292
|
return {
|
|
366
293
|
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
|
|
367
|
-
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
|
|
368
|
-
" \"" + FeeModes("\"\n\"") + "\""},
|
|
369
|
-
{"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125-replaceable.\n"
|
|
370
|
-
"Allows this transaction to be replaced by a transaction with higher fees"},
|
|
371
294
|
{"solving_data", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "Keys and scripts needed for producing a final transaction with a dummy signature.\n"
|
|
372
295
|
"Used for fee estimation during coin selection.",
|
|
373
296
|
{
|
|
@@ -427,7 +350,6 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
|
|
|
427
350
|
{"solving_data", UniValueType(UniValue::VOBJ)},
|
|
428
351
|
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
|
|
429
352
|
{"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
|
|
430
|
-
{"replaceable", UniValueType(UniValue::VBOOL)},
|
|
431
353
|
{"conf_target", UniValueType(UniValue::VNUM)},
|
|
432
354
|
{"estimate_mode", UniValueType(UniValue::VSTR)},
|
|
433
355
|
{"input_weights", UniValueType(UniValue::VARR)},
|
|
@@ -443,7 +365,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
|
|
|
443
365
|
CTxDestination dest = DecodeDestination(change_address_str);
|
|
444
366
|
|
|
445
367
|
if (!IsValidDestination(dest)) {
|
|
446
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Change address must be a valid
|
|
368
|
+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Change address must be a valid peercoin address");
|
|
447
369
|
}
|
|
448
370
|
|
|
449
371
|
coinControl.destChange = dest;
|
|
@@ -485,17 +407,11 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
|
|
|
485
407
|
if (options.exists("estimate_mode")) {
|
|
486
408
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
|
|
487
409
|
}
|
|
488
|
-
coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
|
|
489
|
-
coinControl.fOverrideFeeRate = true;
|
|
490
410
|
}
|
|
491
411
|
|
|
492
412
|
if (options.exists("subtractFeeFromOutputs") || options.exists("subtract_fee_from_outputs") )
|
|
493
413
|
subtractFeeFromOutputs = (options.exists("subtract_fee_from_outputs") ? options["subtract_fee_from_outputs"] : options["subtractFeeFromOutputs"]).get_array();
|
|
494
414
|
|
|
495
|
-
if (options.exists("replaceable")) {
|
|
496
|
-
coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
|
|
497
|
-
}
|
|
498
|
-
SetFeeEstimateMode(wallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
|
|
499
415
|
}
|
|
500
416
|
} else {
|
|
501
417
|
// if options is null and not a bool
|
|
@@ -659,7 +575,7 @@ RPCHelpMan fundrawtransaction()
|
|
|
659
575
|
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
|
|
660
576
|
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
|
|
661
577
|
"If that happens, you will need to fund the transaction with different inputs and republish it."},
|
|
662
|
-
{"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"pool address"}, "The
|
|
578
|
+
{"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"pool address"}, "The peercoin address to receive the change"},
|
|
663
579
|
{"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
|
|
664
580
|
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
|
|
665
581
|
{"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
|
|
@@ -670,7 +586,7 @@ RPCHelpMan fundrawtransaction()
|
|
|
670
586
|
{"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
|
|
671
587
|
{"subtractFeeFromOutputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The integers.\n"
|
|
672
588
|
"The fee will be equally deducted from the amount of each specified output.\n"
|
|
673
|
-
"Those recipients will receive less
|
|
589
|
+
"Those recipients will receive less peercoins than you enter in their corresponding amount field.\n"
|
|
674
590
|
"If no outputs are specified here, the sender pays the fee.",
|
|
675
591
|
{
|
|
676
592
|
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
|
|
@@ -845,183 +761,6 @@ RPCHelpMan signrawtransactionwithwallet()
|
|
|
845
761
|
};
|
|
846
762
|
}
|
|
847
763
|
|
|
848
|
-
static RPCHelpMan bumpfee_helper(std::string method_name)
|
|
849
|
-
{
|
|
850
|
-
const bool want_psbt = method_name == "psbtbumpfee";
|
|
851
|
-
const std::string incremental_fee{CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE).ToString(FeeEstimateMode::SAT_VB)};
|
|
852
|
-
|
|
853
|
-
return RPCHelpMan{method_name,
|
|
854
|
-
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
|
|
855
|
-
+ std::string(want_psbt ? "Returns a PSBT instead of creating and signing a new transaction.\n" : "") +
|
|
856
|
-
"An opt-in RBF transaction with the given txid must be in the wallet.\n"
|
|
857
|
-
"The command will pay the additional fee by reducing change outputs or adding inputs when necessary.\n"
|
|
858
|
-
"It may add a new change output if one does not already exist.\n"
|
|
859
|
-
"All inputs in the original transaction will be included in the replacement transaction.\n"
|
|
860
|
-
"The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
|
|
861
|
-
"By default, the new fee will be calculated automatically using the estimatesmartfee RPC.\n"
|
|
862
|
-
"The user can specify a confirmation target for estimatesmartfee.\n"
|
|
863
|
-
"Alternatively, the user can specify a fee rate in " + CURRENCY_ATOM + "/vB for the new transaction.\n"
|
|
864
|
-
"At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
|
|
865
|
-
"returned by getnetworkinfo) to enter the node's mempool.\n"
|
|
866
|
-
"* WARNING: before version 0.21, fee_rate was in " + CURRENCY_UNIT + "/kvB. As of 0.21, fee_rate is in " + CURRENCY_ATOM + "/vB. *\n",
|
|
867
|
-
{
|
|
868
|
-
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
|
|
869
|
-
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
|
|
870
|
-
{
|
|
871
|
-
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks\n"},
|
|
872
|
-
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"},
|
|
873
|
-
"\nSpecify a fee rate in " + CURRENCY_ATOM + "/vB instead of relying on the built-in fee estimator.\n"
|
|
874
|
-
"Must be at least " + incremental_fee + " higher than the current transaction fee rate.\n"
|
|
875
|
-
"WARNING: before version 0.21, fee_rate was in " + CURRENCY_UNIT + "/kvB. As of 0.21, fee_rate is in " + CURRENCY_ATOM + "/vB.\n"},
|
|
876
|
-
{"replaceable", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether the new transaction should still be\n"
|
|
877
|
-
"marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n"
|
|
878
|
-
"be left unchanged from the original. If false, any input sequence numbers in the\n"
|
|
879
|
-
"original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n"
|
|
880
|
-
"so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
|
|
881
|
-
"still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
|
|
882
|
-
"are replaceable).\n"},
|
|
883
|
-
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, "The fee estimate mode, must be one of (case insensitive):\n"
|
|
884
|
-
"\"" + FeeModes("\"\n\"") + "\""},
|
|
885
|
-
},
|
|
886
|
-
"options"},
|
|
887
|
-
},
|
|
888
|
-
RPCResult{
|
|
889
|
-
RPCResult::Type::OBJ, "", "", Cat(
|
|
890
|
-
want_psbt ?
|
|
891
|
-
std::vector<RPCResult>{{RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction."}} :
|
|
892
|
-
std::vector<RPCResult>{{RPCResult::Type::STR_HEX, "txid", "The id of the new transaction."}},
|
|
893
|
-
{
|
|
894
|
-
{RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
|
|
895
|
-
{RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
|
|
896
|
-
{RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).",
|
|
897
|
-
{
|
|
898
|
-
{RPCResult::Type::STR, "", ""},
|
|
899
|
-
}},
|
|
900
|
-
})
|
|
901
|
-
},
|
|
902
|
-
RPCExamples{
|
|
903
|
-
"\nBump the fee, get the new transaction\'s " + std::string(want_psbt ? "psbt" : "txid") + "\n" +
|
|
904
|
-
HelpExampleCli(method_name, "<txid>")
|
|
905
|
-
},
|
|
906
|
-
[want_psbt](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
907
|
-
{
|
|
908
|
-
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
|
909
|
-
if (!pwallet) return NullUniValue;
|
|
910
|
-
|
|
911
|
-
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !want_psbt) {
|
|
912
|
-
throw JSONRPCError(RPC_WALLET_ERROR, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead.");
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
|
|
916
|
-
uint256 hash(ParseHashV(request.params[0], "txid"));
|
|
917
|
-
|
|
918
|
-
CCoinControl coin_control;
|
|
919
|
-
coin_control.fAllowWatchOnly = pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
|
920
|
-
// optional parameters
|
|
921
|
-
coin_control.m_signal_bip125_rbf = true;
|
|
922
|
-
|
|
923
|
-
if (!request.params[1].isNull()) {
|
|
924
|
-
UniValue options = request.params[1];
|
|
925
|
-
RPCTypeCheckObj(options,
|
|
926
|
-
{
|
|
927
|
-
{"confTarget", UniValueType(UniValue::VNUM)},
|
|
928
|
-
{"conf_target", UniValueType(UniValue::VNUM)},
|
|
929
|
-
{"fee_rate", UniValueType()}, // will be checked by AmountFromValue() in SetFeeEstimateMode()
|
|
930
|
-
{"replaceable", UniValueType(UniValue::VBOOL)},
|
|
931
|
-
{"estimate_mode", UniValueType(UniValue::VSTR)},
|
|
932
|
-
},
|
|
933
|
-
true, true);
|
|
934
|
-
|
|
935
|
-
if (options.exists("confTarget") && options.exists("conf_target")) {
|
|
936
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and conf_target options should not both be set. Use conf_target (confTarget is deprecated).");
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
auto conf_target = options.exists("confTarget") ? options["confTarget"] : options["conf_target"];
|
|
940
|
-
|
|
941
|
-
if (options.exists("replaceable")) {
|
|
942
|
-
coin_control.m_signal_bip125_rbf = options["replaceable"].get_bool();
|
|
943
|
-
}
|
|
944
|
-
SetFeeEstimateMode(*pwallet, coin_control, conf_target, options["estimate_mode"], options["fee_rate"], /* override_min_fee */ false);
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
// Make sure the results are valid at least up to the most recent block
|
|
948
|
-
// the user could have gotten from another RPC command prior to now
|
|
949
|
-
pwallet->BlockUntilSyncedToCurrentChain();
|
|
950
|
-
|
|
951
|
-
LOCK(pwallet->cs_wallet);
|
|
952
|
-
|
|
953
|
-
EnsureWalletIsUnlocked(*pwallet);
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
std::vector<bilingual_str> errors;
|
|
957
|
-
CAmount old_fee;
|
|
958
|
-
CAmount new_fee;
|
|
959
|
-
CMutableTransaction mtx;
|
|
960
|
-
feebumper::Result res;
|
|
961
|
-
// Targeting feerate bump.
|
|
962
|
-
res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx);
|
|
963
|
-
if (res != feebumper::Result::OK) {
|
|
964
|
-
switch(res) {
|
|
965
|
-
case feebumper::Result::INVALID_ADDRESS_OR_KEY:
|
|
966
|
-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errors[0].original);
|
|
967
|
-
break;
|
|
968
|
-
case feebumper::Result::INVALID_REQUEST:
|
|
969
|
-
throw JSONRPCError(RPC_INVALID_REQUEST, errors[0].original);
|
|
970
|
-
break;
|
|
971
|
-
case feebumper::Result::INVALID_PARAMETER:
|
|
972
|
-
throw JSONRPCError(RPC_INVALID_PARAMETER, errors[0].original);
|
|
973
|
-
break;
|
|
974
|
-
case feebumper::Result::WALLET_ERROR:
|
|
975
|
-
throw JSONRPCError(RPC_WALLET_ERROR, errors[0].original);
|
|
976
|
-
break;
|
|
977
|
-
default:
|
|
978
|
-
throw JSONRPCError(RPC_MISC_ERROR, errors[0].original);
|
|
979
|
-
break;
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
UniValue result(UniValue::VOBJ);
|
|
984
|
-
|
|
985
|
-
// For bumpfee, return the new transaction id.
|
|
986
|
-
// For psbtbumpfee, return the base64-encoded unsigned PSBT of the new transaction.
|
|
987
|
-
if (!want_psbt) {
|
|
988
|
-
if (!feebumper::SignTransaction(*pwallet, mtx)) {
|
|
989
|
-
throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
uint256 txid;
|
|
993
|
-
if (feebumper::CommitTransaction(*pwallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) {
|
|
994
|
-
throw JSONRPCError(RPC_WALLET_ERROR, errors[0].original);
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
result.pushKV("txid", txid.GetHex());
|
|
998
|
-
} else {
|
|
999
|
-
PartiallySignedTransaction psbtx(mtx);
|
|
1000
|
-
bool complete = false;
|
|
1001
|
-
const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, false /* sign */, true /* bip32derivs */);
|
|
1002
|
-
CHECK_NONFATAL(err == TransactionError::OK);
|
|
1003
|
-
CHECK_NONFATAL(!complete);
|
|
1004
|
-
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
|
1005
|
-
ssTx << psbtx;
|
|
1006
|
-
result.pushKV("psbt", EncodeBase64(ssTx.str()));
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
result.pushKV("origfee", ValueFromAmount(old_fee));
|
|
1010
|
-
result.pushKV("fee", ValueFromAmount(new_fee));
|
|
1011
|
-
UniValue result_errors(UniValue::VARR);
|
|
1012
|
-
for (const bilingual_str& error : errors) {
|
|
1013
|
-
result_errors.push_back(error.original);
|
|
1014
|
-
}
|
|
1015
|
-
result.pushKV("errors", result_errors);
|
|
1016
|
-
|
|
1017
|
-
return result;
|
|
1018
|
-
},
|
|
1019
|
-
};
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
RPCHelpMan bumpfee() { return bumpfee_helper("bumpfee"); }
|
|
1023
|
-
RPCHelpMan psbtbumpfee() { return bumpfee_helper("psbtbumpfee"); }
|
|
1024
|
-
|
|
1025
764
|
RPCHelpMan send()
|
|
1026
765
|
{
|
|
1027
766
|
return RPCHelpMan{"send",
|
|
@@ -1034,7 +773,7 @@ RPCHelpMan send()
|
|
|
1034
773
|
{
|
|
1035
774
|
{"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
|
|
1036
775
|
{
|
|
1037
|
-
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the
|
|
776
|
+
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the peercoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
|
|
1038
777
|
},
|
|
1039
778
|
},
|
|
1040
779
|
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
|
|
@@ -1045,8 +784,6 @@ RPCHelpMan send()
|
|
|
1045
784
|
},
|
|
1046
785
|
},
|
|
1047
786
|
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
|
|
1048
|
-
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
|
|
1049
|
-
" \"" + FeeModes("\"\n\"") + "\""},
|
|
1050
787
|
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
|
|
1051
788
|
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
|
|
1052
789
|
Cat<std::vector<RPCArg>>(
|
|
@@ -1056,7 +793,7 @@ RPCHelpMan send()
|
|
|
1056
793
|
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
|
|
1057
794
|
"If that happens, you will need to fund the transaction with different inputs and republish it."},
|
|
1058
795
|
{"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns a serialized transaction which will not be added to the wallet or broadcast"},
|
|
1059
|
-
{"change_address", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The
|
|
796
|
+
{"change_address", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The peercoin address to receive the change"},
|
|
1060
797
|
{"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
|
|
1061
798
|
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
|
|
1062
799
|
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
|
|
@@ -1080,7 +817,7 @@ RPCHelpMan send()
|
|
|
1080
817
|
{"psbt", RPCArg::Type::BOOL, RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
|
|
1081
818
|
{"subtract_fee_from_outputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Outputs to subtract the fee from, specified as integer indices.\n"
|
|
1082
819
|
"The fee will be equally deducted from the amount of each specified output.\n"
|
|
1083
|
-
"Those recipients will receive less
|
|
820
|
+
"Those recipients will receive less peercoins than you enter in their corresponding amount field.\n"
|
|
1084
821
|
"If no outputs are specified here, the sender pays the fee.",
|
|
1085
822
|
{
|
|
1086
823
|
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
|
|
@@ -1167,11 +904,7 @@ RPCHelpMan send()
|
|
|
1167
904
|
|
|
1168
905
|
CAmount fee;
|
|
1169
906
|
int change_position;
|
|
1170
|
-
|
|
1171
|
-
if (options.exists("replaceable")) {
|
|
1172
|
-
rbf = options["replaceable"].get_bool();
|
|
1173
|
-
}
|
|
1174
|
-
CMutableTransaction rawTx = ConstructTransaction(options["inputs"], request.params[0], options["locktime"], rbf);
|
|
907
|
+
CMutableTransaction rawTx = ConstructTransaction(options["inputs"], request.params[0], options["locktime"], false);
|
|
1175
908
|
CCoinControl coin_control;
|
|
1176
909
|
// Automatically select coins, unless at least one is manually selected. Can
|
|
1177
910
|
// be overridden by options.add_inputs.
|
|
@@ -1333,7 +1066,7 @@ RPCHelpMan walletcreatefundedpsbt()
|
|
|
1333
1066
|
{
|
|
1334
1067
|
{"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
|
|
1335
1068
|
{
|
|
1336
|
-
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the
|
|
1069
|
+
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the peercoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
|
|
1337
1070
|
},
|
|
1338
1071
|
},
|
|
1339
1072
|
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
|
|
@@ -1344,6 +1077,7 @@ RPCHelpMan walletcreatefundedpsbt()
|
|
|
1344
1077
|
},
|
|
1345
1078
|
},
|
|
1346
1079
|
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
|
|
1080
|
+
{"timestamp", RPCArg::Type::NUM, RPCArg::Default{0}, "Transaction timestamp"},
|
|
1347
1081
|
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
|
|
1348
1082
|
Cat<std::vector<RPCArg>>(
|
|
1349
1083
|
{
|
|
@@ -1351,7 +1085,7 @@ RPCHelpMan walletcreatefundedpsbt()
|
|
|
1351
1085
|
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
|
|
1352
1086
|
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
|
|
1353
1087
|
"If that happens, you will need to fund the transaction with different inputs and republish it."},
|
|
1354
|
-
{"changeAddress", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The
|
|
1088
|
+
{"changeAddress", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The peercoin address to receive the change"},
|
|
1355
1089
|
{"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
|
|
1356
1090
|
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
|
|
1357
1091
|
{"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only"},
|
|
@@ -1360,7 +1094,7 @@ RPCHelpMan walletcreatefundedpsbt()
|
|
|
1360
1094
|
{"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
|
|
1361
1095
|
{"subtractFeeFromOutputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "The outputs to subtract the fee from.\n"
|
|
1362
1096
|
"The fee will be equally deducted from the amount of each specified output.\n"
|
|
1363
|
-
"Those recipients will receive less
|
|
1097
|
+
"Those recipients will receive less peercoins than you enter in their corresponding amount field.\n"
|
|
1364
1098
|
"If no outputs are specified here, the sender pays the fee.",
|
|
1365
1099
|
{
|
|
1366
1100
|
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
|
|
@@ -1397,6 +1131,7 @@ RPCHelpMan walletcreatefundedpsbt()
|
|
|
1397
1131
|
UniValue::VARR,
|
|
1398
1132
|
UniValueType(), // ARR or OBJ, checked later
|
|
1399
1133
|
UniValue::VNUM,
|
|
1134
|
+
UniValue::VNUM,
|
|
1400
1135
|
UniValue::VOBJ,
|
|
1401
1136
|
UniValue::VBOOL
|
|
1402
1137
|
}, true
|
|
@@ -1406,25 +1141,19 @@ RPCHelpMan walletcreatefundedpsbt()
|
|
|
1406
1141
|
|
|
1407
1142
|
CAmount fee;
|
|
1408
1143
|
int change_position;
|
|
1409
|
-
|
|
1410
|
-
const UniValue &replaceable_arg = options["replaceable"];
|
|
1411
|
-
if (!replaceable_arg.isNull()) {
|
|
1412
|
-
RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL);
|
|
1413
|
-
rbf = replaceable_arg.isTrue();
|
|
1414
|
-
}
|
|
1415
|
-
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
|
|
1144
|
+
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
|
|
1416
1145
|
CCoinControl coin_control;
|
|
1417
1146
|
// Automatically select coins, unless at least one is manually selected. Can
|
|
1418
1147
|
// be overridden by options.add_inputs.
|
|
1419
1148
|
coin_control.m_add_inputs = rawTx.vin.size() == 0;
|
|
1420
1149
|
SetOptionsInputWeights(request.params[0], options);
|
|
1421
|
-
FundTransaction(wallet, rawTx, fee, change_position,
|
|
1150
|
+
FundTransaction(wallet, rawTx, fee, change_position, request.params[4], coin_control, /* override_min_fee */ true);
|
|
1422
1151
|
|
|
1423
1152
|
// Make a blank psbt
|
|
1424
1153
|
PartiallySignedTransaction psbtx(rawTx);
|
|
1425
1154
|
|
|
1426
1155
|
// Fill transaction with out data but don't sign
|
|
1427
|
-
bool bip32derivs = request.params[
|
|
1156
|
+
bool bip32derivs = request.params[5].isNull() ? true : request.params[5].get_bool();
|
|
1428
1157
|
bool complete = true;
|
|
1429
1158
|
const TransactionError err{wallet.FillPSBT(psbtx, complete, 1, false, bip32derivs)};
|
|
1430
1159
|
if (err != TransactionError::OK) {
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
#include <core_io.h>
|
|
6
6
|
#include <key_io.h>
|
|
7
|
-
#include <policy/rbf.h>
|
|
8
7
|
#include <rpc/util.h>
|
|
9
8
|
#include <util/vector.h>
|
|
10
9
|
#include <wallet/receive.h>
|
|
@@ -41,17 +40,6 @@ static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue
|
|
|
41
40
|
entry.pushKV("time", wtx.GetTxTime());
|
|
42
41
|
entry.pushKV("timereceived", int64_t{wtx.nTimeReceived});
|
|
43
42
|
|
|
44
|
-
// Add opt-in RBF status
|
|
45
|
-
std::string rbfStatus = "no";
|
|
46
|
-
if (confirms <= 0) {
|
|
47
|
-
RBFTransactionState rbfState = chain.isRBFOptIn(*wtx.tx);
|
|
48
|
-
if (rbfState == RBFTransactionState::UNKNOWN)
|
|
49
|
-
rbfStatus = "unknown";
|
|
50
|
-
else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)
|
|
51
|
-
rbfStatus = "yes";
|
|
52
|
-
}
|
|
53
|
-
entry.pushKV("bip125-replaceable", rbfStatus);
|
|
54
|
-
|
|
55
43
|
for (const std::pair<const std::string, std::string>& item : wtx.mapValue)
|
|
56
44
|
entry.pushKV(item.first, item.second);
|
|
57
45
|
}
|
|
@@ -330,6 +318,16 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
|
|
|
330
318
|
}
|
|
331
319
|
}
|
|
332
320
|
|
|
321
|
+
static void PushCoinStakeCategory(UniValue & entry, const CWalletTx &wtx, const CWallet& wallet)
|
|
322
|
+
{
|
|
323
|
+
if (wallet.GetTxDepthInMainChain(wtx) < 1)
|
|
324
|
+
entry.pushKV("category", "stake-orphan");
|
|
325
|
+
else if (wallet.GetTxBlocksToMaturity(wtx) > 0)
|
|
326
|
+
entry.pushKV("category", "stake");
|
|
327
|
+
else
|
|
328
|
+
entry.pushKV("category", "stake-mint");
|
|
329
|
+
}
|
|
330
|
+
|
|
333
331
|
/**
|
|
334
332
|
* List transactions based on the given criteria.
|
|
335
333
|
*
|
|
@@ -361,7 +359,10 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
|
|
|
361
359
|
entry.pushKV("involvesWatchonly", true);
|
|
362
360
|
}
|
|
363
361
|
MaybePushAddress(entry, s.destination);
|
|
364
|
-
|
|
362
|
+
if (wtx.IsCoinStake())
|
|
363
|
+
PushCoinStakeCategory(entry, wtx, wallet);
|
|
364
|
+
else
|
|
365
|
+
entry.pushKV("category", "send");
|
|
365
366
|
entry.pushKV("amount", ValueFromAmount(-s.amount));
|
|
366
367
|
const auto* address_book_entry = wallet.FindAddressBookEntry(s.destination);
|
|
367
368
|
if (address_book_entry) {
|
|
@@ -402,6 +403,10 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
|
|
|
402
403
|
else
|
|
403
404
|
entry.pushKV("category", "generate");
|
|
404
405
|
}
|
|
406
|
+
else if (wtx.IsCoinStake())
|
|
407
|
+
{
|
|
408
|
+
PushCoinStakeCategory(entry, wtx, wallet);
|
|
409
|
+
}
|
|
405
410
|
else
|
|
406
411
|
{
|
|
407
412
|
entry.pushKV("category", "receive");
|
|
@@ -418,7 +423,6 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
|
|
|
418
423
|
}
|
|
419
424
|
}
|
|
420
425
|
|
|
421
|
-
|
|
422
426
|
static const std::vector<RPCResult> TransactionDescriptionString()
|
|
423
427
|
{
|
|
424
428
|
return{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n"
|
|
@@ -435,8 +439,6 @@ static const std::vector<RPCResult> TransactionDescriptionString()
|
|
|
435
439
|
{
|
|
436
440
|
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
|
|
437
441
|
}},
|
|
438
|
-
{RPCResult::Type::STR_HEX, "replaced_by_txid", /*optional=*/true, "The txid if this tx was replaced."},
|
|
439
|
-
{RPCResult::Type::STR_HEX, "replaces_txid", /*optional=*/true, "The txid if the tx replaces one."},
|
|
440
442
|
{RPCResult::Type::STR, "comment", /*optional=*/true, ""},
|
|
441
443
|
{RPCResult::Type::STR, "to", /*optional=*/true, "If a comment to is associated with the transaction."},
|
|
442
444
|
{RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
|
|
@@ -567,8 +569,7 @@ RPCHelpMan listsinceblock()
|
|
|
567
569
|
{"blockhash", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, the block hash to list transactions since, otherwise list all transactions."},
|
|
568
570
|
{"target_confirmations", RPCArg::Type::NUM, RPCArg::Default{1}, "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
|
|
569
571
|
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Include transactions to watch-only addresses (see 'importaddress')"},
|
|
570
|
-
{"include_removed", RPCArg::Type::BOOL, RPCArg::Default{true}, "Show transactions that were removed due to a reorg in the \"removed\" array
|
|
571
|
-
"(not guaranteed to work on pruned nodes)"},
|
|
572
|
+
{"include_removed", RPCArg::Type::BOOL, RPCArg::Default{true}, "Show transactions that were removed due to a reorg in the \"removed\" array"},
|
|
572
573
|
},
|
|
573
574
|
RPCResult{
|
|
574
575
|
RPCResult::Type::OBJ, "", "",
|
|
@@ -908,7 +909,7 @@ RPCHelpMan rescanblockchain()
|
|
|
908
909
|
|
|
909
910
|
// We can't rescan beyond non-pruned blocks, stop and throw an error
|
|
910
911
|
if (!pwallet->chain().hasBlocks(pwallet->GetLastBlockHash(), start_height, stop_height)) {
|
|
911
|
-
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
|
|
912
|
+
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height."); // peercoin: should never happen
|
|
912
913
|
}
|
|
913
914
|
|
|
914
915
|
CHECK_NONFATAL(pwallet->chain().findAncestorByHeight(pwallet->GetLastBlockHash(), start_height, FoundBlock().hash(start_block)));
|
|
@@ -82,6 +82,9 @@ void EnsureWalletIsUnlocked(const CWallet& wallet)
|
|
|
82
82
|
if (wallet.IsLocked()) {
|
|
83
83
|
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
|
|
84
84
|
}
|
|
85
|
+
if (fWalletUnlockMintOnly) {
|
|
86
|
+
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet unlocked for block minting only.");
|
|
87
|
+
}
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
WalletContext& EnsureWalletContext(const std::any& context)
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
5
|
|
|
6
6
|
#include <core_io.h>
|
|
7
|
+
//#include <interfaces/wallet.h>
|
|
7
8
|
#include <key_io.h>
|
|
8
9
|
#include <rpc/server.h>
|
|
10
|
+
#include <rpc/server_util.h>
|
|
9
11
|
#include <rpc/util.h>
|
|
12
|
+
#include <timedata.h>
|
|
10
13
|
#include <util/translation.h>
|
|
11
14
|
#include <wallet/receive.h>
|
|
12
15
|
#include <wallet/rpc/wallet.h>
|
|
@@ -17,6 +20,11 @@
|
|
|
17
20
|
|
|
18
21
|
#include <univalue.h>
|
|
19
22
|
|
|
23
|
+
#include <kernelrecord.h>
|
|
24
|
+
#include <node/miner.h>
|
|
25
|
+
#include <boost/lexical_cast.hpp>
|
|
26
|
+
|
|
27
|
+
using wallet::WalletContext;
|
|
20
28
|
|
|
21
29
|
namespace wallet {
|
|
22
30
|
/** Checks if a CKey is in the given CWallet compressed or otherwise*/
|
|
@@ -105,8 +113,8 @@ static RPCHelpMan getwalletinfo()
|
|
|
105
113
|
}
|
|
106
114
|
if (pwallet->IsCrypted()) {
|
|
107
115
|
obj.pushKV("unlocked_until", pwallet->nRelockTime);
|
|
116
|
+
obj.pushKV("unlocked_minting_only", fWalletUnlockMintOnly);
|
|
108
117
|
}
|
|
109
|
-
obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
|
|
110
118
|
obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
|
|
111
119
|
obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
|
|
112
120
|
if (pwallet->IsScanning()) {
|
|
@@ -196,7 +204,7 @@ static RPCHelpMan loadwallet()
|
|
|
196
204
|
{
|
|
197
205
|
return RPCHelpMan{"loadwallet",
|
|
198
206
|
"\nLoads a wallet from a wallet file or directory."
|
|
199
|
-
"\nNote that all wallet command-line options used when starting
|
|
207
|
+
"\nNote that all wallet command-line options used when starting peercoind will be"
|
|
200
208
|
"\napplied to the new wallet.\n",
|
|
201
209
|
{
|
|
202
210
|
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
|
|
@@ -450,6 +458,254 @@ static RPCHelpMan unloadwallet()
|
|
|
450
458
|
};
|
|
451
459
|
}
|
|
452
460
|
|
|
461
|
+
static RPCHelpMan importcoinstake()
|
|
462
|
+
{
|
|
463
|
+
return RPCHelpMan{"importcoinstake",
|
|
464
|
+
"Import presigned coinstake for use in minting.\n",
|
|
465
|
+
{
|
|
466
|
+
{"coinstake", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"signed coinstake"}, "signed coinstake transaction as hex."},
|
|
467
|
+
{"timestamp", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "timestamp when this coinstake will be valid."},
|
|
468
|
+
},
|
|
469
|
+
RPCResult{RPCResult::Type::OBJ, "", "", {
|
|
470
|
+
{RPCResult::Type::STR, "txid", "transaction id if import is successful."},
|
|
471
|
+
{RPCResult::Type::NUM, "nTime", "timestamp when coinstake is due to mint."},
|
|
472
|
+
}},
|
|
473
|
+
RPCExamples{
|
|
474
|
+
HelpExampleCli("importcoinstake", "03000000")
|
|
475
|
+
+ HelpExampleRpc("importcoinstake", "03000000")
|
|
476
|
+
},
|
|
477
|
+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
478
|
+
{
|
|
479
|
+
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
|
480
|
+
CWallet* const pwallet = wallet.get();
|
|
481
|
+
|
|
482
|
+
RPCTypeCheck(request.params, {
|
|
483
|
+
UniValue::VSTR,
|
|
484
|
+
UniValue::VNUM
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// parse hex string from parameter
|
|
488
|
+
CMutableTransaction mtx;
|
|
489
|
+
if (!DecodeHexTx(mtx, request.params[0].get_str()))
|
|
490
|
+
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
|
491
|
+
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
|
|
492
|
+
|
|
493
|
+
std::string err_string;
|
|
494
|
+
AssertLockNotHeld(cs_main);
|
|
495
|
+
|
|
496
|
+
{
|
|
497
|
+
int timestamp;
|
|
498
|
+
if (!request.params[1].isNull())
|
|
499
|
+
timestamp = request.params[1].get_int();
|
|
500
|
+
else
|
|
501
|
+
timestamp = tx->nTime;
|
|
502
|
+
|
|
503
|
+
if (timestamp < GetTime()) {
|
|
504
|
+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Expired coinstake");
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// check if we have the key to vout[1]
|
|
508
|
+
std::set<ScriptPubKeyMan*> spk_mans = pwallet->GetScriptPubKeyMans(tx->vout[1].scriptPubKey);
|
|
509
|
+
if (spk_mans.size() == 0) {
|
|
510
|
+
throw JSONRPCError(RPC_INVALID_PARAMETER, "No keys for vout[1]");
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// add to in memory structure
|
|
514
|
+
pwallet->m_coinstakes[timestamp] = tx;
|
|
515
|
+
}
|
|
516
|
+
UniValue result(UniValue::VOBJ);
|
|
517
|
+
result.pushKV("txid", tx->GetHash().GetHex());
|
|
518
|
+
result.pushKV("nTime", int(tx->nTime));
|
|
519
|
+
return result;
|
|
520
|
+
},
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
static RPCHelpMan listminting()
|
|
526
|
+
{
|
|
527
|
+
return RPCHelpMan{"listminting",
|
|
528
|
+
"Return all mintable outputs and provide details for each of them.\n",
|
|
529
|
+
{
|
|
530
|
+
{"count", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "maximum number of outputs to be returned."},
|
|
531
|
+
},
|
|
532
|
+
RPCResult{
|
|
533
|
+
RPCResult::Type::ARR, "", "",
|
|
534
|
+
{
|
|
535
|
+
{RPCResult::Type::OBJ, "", "",
|
|
536
|
+
{
|
|
537
|
+
{RPCResult::Type::STR, "address", "Address of the output"},
|
|
538
|
+
{RPCResult::Type::STR, "input-txid", "Transaction id"},
|
|
539
|
+
{RPCResult::Type::NUM, "time", "Time of transaction"},
|
|
540
|
+
{RPCResult::Type::NUM, "amount", "Amount of transaction output"},
|
|
541
|
+
{RPCResult::Type::STR, "status", "Status of transaction output"},
|
|
542
|
+
{RPCResult::Type::NUM, "age-in-day", "Age of transaction in days"},
|
|
543
|
+
{RPCResult::Type::NUM, "coin-day-weight", "Weight of transaction output"},
|
|
544
|
+
{RPCResult::Type::NUM, "proof-of-stake-difficulty", "Current proof of stake difficulty"},
|
|
545
|
+
{RPCResult::Type::NUM, "minting-probability-10min", "Probability of minting in next 10 minutes"},
|
|
546
|
+
{RPCResult::Type::NUM, "minting-probability-24h", "Probability of minting in next 24 hours"},
|
|
547
|
+
{RPCResult::Type::NUM, "minting-probability-30d", "Probability of minting in next 30 days"},
|
|
548
|
+
{RPCResult::Type::NUM, "minting-probability-90d", "Probability of minting in next 90 days"},
|
|
549
|
+
{RPCResult::Type::NUM, "search-interval-in-sec", "Interval between last minting attempts"},
|
|
550
|
+
{RPCResult::Type::NUM, "attempts", "Number of seconds since maturity"},
|
|
551
|
+
}},
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
RPCExamples{
|
|
555
|
+
HelpExampleCli("listminting", "10")
|
|
556
|
+
+ HelpExampleRpc("listminting", "10")
|
|
557
|
+
},
|
|
558
|
+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
559
|
+
{
|
|
560
|
+
WalletContext& context = EnsureWalletContext(request.context);
|
|
561
|
+
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
|
562
|
+
CWallet* const pwallet = wallet.get();
|
|
563
|
+
|
|
564
|
+
RPCTypeCheck(request.params, {
|
|
565
|
+
UniValue::VNUM
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
int64_t count=-1;
|
|
569
|
+
if (!request.params[0].isNull())
|
|
570
|
+
count = request.params[0].get_int();
|
|
571
|
+
|
|
572
|
+
UniValue ret(UniValue::VARR);
|
|
573
|
+
|
|
574
|
+
const CBlockIndex *p = GetLastBlockIndex(context.chain->chainman().ActiveChain().Tip(), true);
|
|
575
|
+
double difficulty = p->GetBlockDifficulty();
|
|
576
|
+
int64_t nStakeMinAge = Params().GetConsensus().nStakeMinAge;
|
|
577
|
+
|
|
578
|
+
std::unique_ptr<interfaces::Wallet> iwallet = interfaces::MakeWallet(context,wallet);
|
|
579
|
+
const auto& vwtx = iwallet->getWalletTxs();
|
|
580
|
+
for(const auto& wtx : vwtx) {
|
|
581
|
+
std::vector<KernelRecord> txList = KernelRecord::decomposeOutput(*iwallet, wtx);
|
|
582
|
+
|
|
583
|
+
int64_t minAge = nStakeMinAge / 60 / 60 / 24;
|
|
584
|
+
for (auto& kr : txList) {
|
|
585
|
+
if(!kr.spent) {
|
|
586
|
+
|
|
587
|
+
if(count > 0 && (int32_t)ret.size() >= count) {
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
std::string strTime = boost::lexical_cast<std::string>(kr.nTime);
|
|
592
|
+
std::string strAmount = boost::lexical_cast<std::string>(kr.nValue);
|
|
593
|
+
std::string strAge = boost::lexical_cast<std::string>(kr.getAge());
|
|
594
|
+
std::string strCoinAge = boost::lexical_cast<std::string>(kr.getCoinAge());
|
|
595
|
+
|
|
596
|
+
// JSONRPCRequest request2;
|
|
597
|
+
// request2.params = UniValue(UniValue::VARR);
|
|
598
|
+
// request2.params.push_back(kr.address);
|
|
599
|
+
// std::string account = AccountFromValue(getaccount(request2));
|
|
600
|
+
|
|
601
|
+
std::string status = "immature";
|
|
602
|
+
int searchInterval = 0;
|
|
603
|
+
int attemps = 0;
|
|
604
|
+
if(kr.getAge() >= minAge)
|
|
605
|
+
{
|
|
606
|
+
status = "mature";
|
|
607
|
+
searchInterval = (int)nLastCoinStakeSearchInterval;
|
|
608
|
+
attemps = GetAdjustedTime() - kr.nTime - nStakeMinAge;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
UniValue obj(UniValue::VOBJ);
|
|
612
|
+
// obj.push_back(Pair("account", account));
|
|
613
|
+
obj.pushKV("address", kr.address);
|
|
614
|
+
obj.pushKV("input-txid", kr.hash.ToString());
|
|
615
|
+
obj.pushKV("time", strTime);
|
|
616
|
+
obj.pushKV("amount", strAmount);
|
|
617
|
+
obj.pushKV("status", status);
|
|
618
|
+
obj.pushKV("age-in-day", strAge);
|
|
619
|
+
obj.pushKV("coin-day-weight", strCoinAge);
|
|
620
|
+
obj.pushKV("proof-of-stake-difficulty", difficulty);
|
|
621
|
+
obj.pushKV("minting-probability-10min", kr.getProbToMintWithinNMinutes(difficulty, 10));
|
|
622
|
+
obj.pushKV("minting-probability-24h", kr.getProbToMintWithinNMinutes(difficulty, 60*24));
|
|
623
|
+
obj.pushKV("minting-probability-30d", kr.getProbToMintWithinNMinutes(difficulty, 60*24*30));
|
|
624
|
+
obj.pushKV("minting-probability-90d", kr.getProbToMintWithinNMinutes(difficulty, 60*24*90));
|
|
625
|
+
obj.pushKV("search-interval-in-sec", searchInterval);
|
|
626
|
+
obj.pushKV("attempts", attemps);
|
|
627
|
+
ret.push_back(obj);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (pwallet->m_coinstakes.size()) {
|
|
633
|
+
for (const auto& [timestamp, txn] : pwallet->m_coinstakes) {
|
|
634
|
+
UniValue obj(UniValue::VOBJ);
|
|
635
|
+
CTxDestination address;
|
|
636
|
+
ExtractDestination(txn->vout[1].scriptPubKey, address);
|
|
637
|
+
obj.pushKV("address", EncodeDestination(address));
|
|
638
|
+
obj.pushKV("amount", ValueFromAmount(txn->vout[1].nValue));
|
|
639
|
+
obj.pushKV("status", "imported");
|
|
640
|
+
obj.pushKV("time", (uint64_t)txn->nTime);
|
|
641
|
+
obj.pushKV("due-in-seconds", (uint64_t)(txn->nTime - GetAdjustedTime()));
|
|
642
|
+
ret.push_back(obj);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return ret;
|
|
646
|
+
},
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
static RPCHelpMan reservebalance()
|
|
651
|
+
{
|
|
652
|
+
return RPCHelpMan{"reservebalance",
|
|
653
|
+
"Set reserve amount not participating in network protection.\n",
|
|
654
|
+
{
|
|
655
|
+
{"reserve", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "turn balance reserve on or off."},
|
|
656
|
+
{"amount", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "amount of peercoin to be reserved."},
|
|
657
|
+
},
|
|
658
|
+
RPCResult{RPCResult::Type::OBJ, "", "", {
|
|
659
|
+
{RPCResult::Type::STR, "reserve", "status of reserve."},
|
|
660
|
+
}},
|
|
661
|
+
RPCExamples{
|
|
662
|
+
HelpExampleCli("reservebalance", "true 10")
|
|
663
|
+
+ HelpExampleRpc("reservebalance", "true 10")
|
|
664
|
+
},
|
|
665
|
+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
|
666
|
+
{
|
|
667
|
+
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
|
668
|
+
CWallet* const pwallet = wallet.get();
|
|
669
|
+
|
|
670
|
+
EnsureWalletIsUnlocked(*pwallet);
|
|
671
|
+
|
|
672
|
+
RPCTypeCheck(request.params, {
|
|
673
|
+
UniValue::VBOOL,
|
|
674
|
+
UniValue::VNUM
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
if (request.params.size() > 0)
|
|
678
|
+
{
|
|
679
|
+
bool fReserve = request.params[0].get_bool();
|
|
680
|
+
if (fReserve)
|
|
681
|
+
{
|
|
682
|
+
if (request.params.size() == 1)
|
|
683
|
+
throw std::runtime_error("must provide amount to reserve balance.\n");
|
|
684
|
+
int64_t nAmount = AmountFromValue(request.params[1]);
|
|
685
|
+
nAmount = (nAmount / CENT) * CENT; // round to cent
|
|
686
|
+
if (nAmount < 0)
|
|
687
|
+
throw std::runtime_error("amount cannot be negative.\n");
|
|
688
|
+
gArgs.ForceSetArg("-reservebalance", FormatMoney(nAmount));
|
|
689
|
+
}
|
|
690
|
+
else
|
|
691
|
+
{
|
|
692
|
+
if (request.params.size() > 1)
|
|
693
|
+
throw std::runtime_error("cannot specify amount to turn off reserve.\n");
|
|
694
|
+
gArgs.ForceSetArg("-reservebalance", "0");
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
UniValue result(UniValue::VOBJ);
|
|
699
|
+
std::optional<CAmount> nReserveBalance = ParseMoney(gArgs.GetArg("-reservebalance", ""));
|
|
700
|
+
if (gArgs.IsArgSet("-reservebalance") && !nReserveBalance)
|
|
701
|
+
throw std::runtime_error("invalid reserve balance amount\n");
|
|
702
|
+
result.pushKV("reserve", (nReserveBalance > 0));
|
|
703
|
+
result.pushKV("amount", nReserveBalance.value());
|
|
704
|
+
return result;
|
|
705
|
+
},
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
|
|
453
709
|
static RPCHelpMan sethdseed()
|
|
454
710
|
{
|
|
455
711
|
return RPCHelpMan{"sethdseed",
|
|
@@ -607,8 +863,6 @@ RPCHelpMan importaddress();
|
|
|
607
863
|
RPCHelpMan importpubkey();
|
|
608
864
|
RPCHelpMan dumpwallet();
|
|
609
865
|
RPCHelpMan importwallet();
|
|
610
|
-
RPCHelpMan importprunedfunds();
|
|
611
|
-
RPCHelpMan removeprunedfunds();
|
|
612
866
|
RPCHelpMan importmulti();
|
|
613
867
|
RPCHelpMan importdescriptors();
|
|
614
868
|
RPCHelpMan listdescriptors();
|
|
@@ -634,10 +888,7 @@ RPCHelpMan encryptwallet();
|
|
|
634
888
|
// spend
|
|
635
889
|
RPCHelpMan sendtoaddress();
|
|
636
890
|
RPCHelpMan sendmany();
|
|
637
|
-
RPCHelpMan settxfee();
|
|
638
891
|
RPCHelpMan fundrawtransaction();
|
|
639
|
-
RPCHelpMan bumpfee();
|
|
640
|
-
RPCHelpMan psbtbumpfee();
|
|
641
892
|
RPCHelpMan send();
|
|
642
893
|
RPCHelpMan walletprocesspsbt();
|
|
643
894
|
RPCHelpMan walletcreatefundedpsbt();
|
|
@@ -667,8 +918,6 @@ static const CRPCCommand commands[] =
|
|
|
667
918
|
{ "wallet", &abortrescan, },
|
|
668
919
|
{ "wallet", &addmultisigaddress, },
|
|
669
920
|
{ "wallet", &backupwallet, },
|
|
670
|
-
{ "wallet", &bumpfee, },
|
|
671
|
-
{ "wallet", &psbtbumpfee, },
|
|
672
921
|
{ "wallet", &createwallet, },
|
|
673
922
|
{ "wallet", &restorewallet, },
|
|
674
923
|
{ "wallet", &dumpprivkey, },
|
|
@@ -689,7 +938,6 @@ static const CRPCCommand commands[] =
|
|
|
689
938
|
{ "wallet", &importdescriptors, },
|
|
690
939
|
{ "wallet", &importmulti, },
|
|
691
940
|
{ "wallet", &importprivkey, },
|
|
692
|
-
{ "wallet", &importprunedfunds, },
|
|
693
941
|
{ "wallet", &importpubkey, },
|
|
694
942
|
{ "wallet", &importwallet, },
|
|
695
943
|
{ "wallet", &keypoolrefill, },
|
|
@@ -707,14 +955,12 @@ static const CRPCCommand commands[] =
|
|
|
707
955
|
{ "wallet", &loadwallet, },
|
|
708
956
|
{ "wallet", &lockunspent, },
|
|
709
957
|
{ "wallet", &newkeypool, },
|
|
710
|
-
{ "wallet", &removeprunedfunds, },
|
|
711
958
|
{ "wallet", &rescanblockchain, },
|
|
712
959
|
{ "wallet", &send, },
|
|
713
960
|
{ "wallet", &sendmany, },
|
|
714
961
|
{ "wallet", &sendtoaddress, },
|
|
715
962
|
{ "wallet", &sethdseed, },
|
|
716
963
|
{ "wallet", &setlabel, },
|
|
717
|
-
{ "wallet", &settxfee, },
|
|
718
964
|
{ "wallet", &setwalletflag, },
|
|
719
965
|
{ "wallet", &signmessage, },
|
|
720
966
|
{ "wallet", &signrawtransactionwithwallet, },
|
|
@@ -728,6 +974,10 @@ static const CRPCCommand commands[] =
|
|
|
728
974
|
{ "wallet", &walletpassphrase, },
|
|
729
975
|
{ "wallet", &walletpassphrasechange, },
|
|
730
976
|
{ "wallet", &walletprocesspsbt, },
|
|
977
|
+
// peercoin commands
|
|
978
|
+
{ "wallet", &importcoinstake, },
|
|
979
|
+
{ "wallet", &listminting, },
|
|
980
|
+
{ "wallet", &reservebalance, },
|
|
731
981
|
};
|
|
732
982
|
// clang-format on
|
|
733
983
|
return commands;
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
#include <interfaces/chain.h>
|
|
8
8
|
#include <policy/policy.h>
|
|
9
9
|
#include <script/signingprovider.h>
|
|
10
|
+
#include <timedata.h>
|
|
10
11
|
#include <util/check.h>
|
|
11
12
|
#include <util/fees.h>
|
|
12
13
|
#include <util/moneystr.h>
|
|
13
14
|
#include <util/rbf.h>
|
|
14
15
|
#include <util/translation.h>
|
|
15
16
|
#include <wallet/coincontrol.h>
|
|
16
|
-
#include <wallet/fees.h>
|
|
17
17
|
#include <wallet/receive.h>
|
|
18
18
|
#include <wallet/spend.h>
|
|
19
19
|
#include <wallet/transaction.h>
|
|
@@ -119,37 +119,6 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
|
|
|
119
119
|
|
|
120
120
|
bool safeTx = CachedTxIsTrusted(wallet, wtx, trusted_parents);
|
|
121
121
|
|
|
122
|
-
// We should not consider coins from transactions that are replacing
|
|
123
|
-
// other transactions.
|
|
124
|
-
//
|
|
125
|
-
// Example: There is a transaction A which is replaced by bumpfee
|
|
126
|
-
// transaction B. In this case, we want to prevent creation of
|
|
127
|
-
// a transaction B' which spends an output of B.
|
|
128
|
-
//
|
|
129
|
-
// Reason: If transaction A were initially confirmed, transactions B
|
|
130
|
-
// and B' would no longer be valid, so the user would have to create
|
|
131
|
-
// a new transaction C to replace B'. However, in the case of a
|
|
132
|
-
// one-block reorg, transactions B' and C might BOTH be accepted,
|
|
133
|
-
// when the user only wanted one of them. Specifically, there could
|
|
134
|
-
// be a 1-block reorg away from the chain where transactions A and C
|
|
135
|
-
// were accepted to another chain where B, B', and C were all
|
|
136
|
-
// accepted.
|
|
137
|
-
if (nDepth == 0 && wtx.mapValue.count("replaces_txid")) {
|
|
138
|
-
safeTx = false;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Similarly, we should not consider coins from transactions that
|
|
142
|
-
// have been replaced. In the example above, we would want to prevent
|
|
143
|
-
// creation of a transaction A' spending an output of A, because if
|
|
144
|
-
// transaction B were initially confirmed, conflicting with A and
|
|
145
|
-
// A', we wouldn't want to the user to create a transaction D
|
|
146
|
-
// intending to replace A', but potentially resulting in a scenario
|
|
147
|
-
// where A, A', and D could all be accepted (instead of just B and
|
|
148
|
-
// D, or just A and A' like the user would want).
|
|
149
|
-
if (nDepth == 0 && wtx.mapValue.count("replaced_by_txid")) {
|
|
150
|
-
safeTx = false;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
122
|
if (only_safe && !safeTx) {
|
|
154
123
|
continue;
|
|
155
124
|
}
|
|
@@ -467,7 +436,7 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
|
|
|
467
436
|
if (coin.m_input_bytes == -1) {
|
|
468
437
|
return std::nullopt; // Not solvable, can't estimate size for fee
|
|
469
438
|
}
|
|
470
|
-
coin.effective_value = coin.txout.nValue -
|
|
439
|
+
coin.effective_value = coin.txout.nValue - GetMinFee(coin.m_input_bytes, GetAdjustedTime());
|
|
471
440
|
if (coin_selection_params.m_subtract_fee_outputs) {
|
|
472
441
|
value_to_select -= coin.txout.nValue;
|
|
473
442
|
} else {
|
|
@@ -636,7 +605,7 @@ static bool CreateTransactionInternal(
|
|
|
636
605
|
int& nChangePosInOut,
|
|
637
606
|
bilingual_str& error,
|
|
638
607
|
const CCoinControl& coin_control,
|
|
639
|
-
|
|
608
|
+
CAmount& fee_calc_out,
|
|
640
609
|
bool sign) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
|
|
641
610
|
{
|
|
642
611
|
AssertLockHeld(wallet.cs_wallet);
|
|
@@ -647,9 +616,6 @@ static bool CreateTransactionInternal(
|
|
|
647
616
|
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
|
|
648
617
|
coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
|
|
649
618
|
|
|
650
|
-
// Set the long term feerate estimate to the wallet's consolidate feerate
|
|
651
|
-
coin_selection_params.m_long_term_feerate = wallet.m_consolidate_feerate;
|
|
652
|
-
|
|
653
619
|
CAmount recipients_sum = 0;
|
|
654
620
|
const OutputType change_type = wallet.TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : wallet.m_default_change_type, vecSend);
|
|
655
621
|
ReserveDestination reservedest(&wallet, change_type);
|
|
@@ -691,7 +657,7 @@ static bool CreateTransactionInternal(
|
|
|
691
657
|
CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty());
|
|
692
658
|
}
|
|
693
659
|
CTxOut change_prototype_txout(0, scriptChange);
|
|
694
|
-
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
|
|
660
|
+
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout, PROTOCOL_VERSION);
|
|
695
661
|
|
|
696
662
|
// Get size of spending the change output
|
|
697
663
|
int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, &wallet);
|
|
@@ -703,31 +669,13 @@ static bool CreateTransactionInternal(
|
|
|
703
669
|
coin_selection_params.change_spend_size = (size_t)change_spend_size;
|
|
704
670
|
}
|
|
705
671
|
|
|
706
|
-
// Set discard feerate
|
|
707
|
-
coin_selection_params.m_discard_feerate = GetDiscardRate(wallet);
|
|
708
|
-
|
|
709
|
-
// Get the fee rate to use effective values in coin selection
|
|
710
|
-
FeeCalculation feeCalc;
|
|
711
|
-
coin_selection_params.m_effective_feerate = GetMinimumFeeRate(wallet, coin_control, &feeCalc);
|
|
712
|
-
// Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
|
|
713
|
-
// provided one
|
|
714
|
-
if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
|
|
715
|
-
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
|
|
716
|
-
return false;
|
|
717
|
-
}
|
|
718
|
-
if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee) {
|
|
719
|
-
// eventually allow a fallback fee
|
|
720
|
-
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
|
|
721
|
-
return false;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
672
|
// Calculate the cost of change
|
|
725
673
|
// Cost of change is the cost of creating the change output + cost of spending the change output in the future.
|
|
726
674
|
// For creating the change output now, we use the effective feerate.
|
|
727
675
|
// For spending the change output in the future, we use the discard feerate for now.
|
|
728
676
|
// So cost of change = (change output size * effective feerate) + (size of spending change output * discard feerate)
|
|
729
|
-
coin_selection_params.m_change_fee =
|
|
730
|
-
coin_selection_params.m_cost_of_change =
|
|
677
|
+
coin_selection_params.m_change_fee = GetMinFee(coin_selection_params.change_output_size, GetAdjustedTime());
|
|
678
|
+
coin_selection_params.m_cost_of_change = GetMinFee(coin_selection_params.change_spend_size, GetAdjustedTime()) + coin_selection_params.m_change_fee;
|
|
731
679
|
|
|
732
680
|
// vouts to the payees
|
|
733
681
|
if (!coin_selection_params.m_subtract_fee_outputs) {
|
|
@@ -742,7 +690,7 @@ static bool CreateTransactionInternal(
|
|
|
742
690
|
coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
|
|
743
691
|
}
|
|
744
692
|
|
|
745
|
-
if (
|
|
693
|
+
if (recipient.nAmount < MIN_TXOUT_AMOUNT)
|
|
746
694
|
{
|
|
747
695
|
error = _("Transaction amount too small");
|
|
748
696
|
return false;
|
|
@@ -751,7 +699,7 @@ static bool CreateTransactionInternal(
|
|
|
751
699
|
}
|
|
752
700
|
|
|
753
701
|
// Include the fees for things that aren't inputs, excluding the change output
|
|
754
|
-
const CAmount not_input_fees = coin_selection_params.
|
|
702
|
+
const CAmount not_input_fees = coin_selection_params.tx_noinputs_size ? GetMinFee(coin_selection_params.tx_noinputs_size, GetAdjustedTime()) : 0;
|
|
755
703
|
CAmount selection_target = recipients_sum + not_input_fees;
|
|
756
704
|
|
|
757
705
|
// Get available coins
|
|
@@ -796,7 +744,7 @@ static bool CreateTransactionInternal(
|
|
|
796
744
|
// to avoid conflicting with other possible uses of nSequence,
|
|
797
745
|
// and in the spirit of "smallest possible change from prior
|
|
798
746
|
// behavior."
|
|
799
|
-
const uint32_t nSequence{
|
|
747
|
+
const uint32_t nSequence{CTxIn::MAX_SEQUENCE_NONFINAL};
|
|
800
748
|
for (const auto& coin : selected_coins) {
|
|
801
749
|
txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
|
|
802
750
|
}
|
|
@@ -808,7 +756,7 @@ static bool CreateTransactionInternal(
|
|
|
808
756
|
error = _("Missing solving data for estimating transaction size");
|
|
809
757
|
return false;
|
|
810
758
|
}
|
|
811
|
-
nFeeRet =
|
|
759
|
+
nFeeRet = GetMinFee(nBytes, GetAdjustedTime());
|
|
812
760
|
|
|
813
761
|
// Subtract fee from the change output if not subtracting it from recipient outputs
|
|
814
762
|
CAmount fee_needed = nFeeRet;
|
|
@@ -820,7 +768,7 @@ static bool CreateTransactionInternal(
|
|
|
820
768
|
// 1. The change output would be dust
|
|
821
769
|
// 2. The change is within the (almost) exact match window, i.e. it is less than or equal to the cost of the change output (cost_of_change)
|
|
822
770
|
CAmount change_amount = change_position->nValue;
|
|
823
|
-
if (
|
|
771
|
+
if ((change_amount < MIN_TXOUT_AMOUNT) || change_amount <= coin_selection_params.m_cost_of_change)
|
|
824
772
|
{
|
|
825
773
|
nChangePosInOut = -1;
|
|
826
774
|
change_amount = 0;
|
|
@@ -829,7 +777,7 @@ static bool CreateTransactionInternal(
|
|
|
829
777
|
// Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
|
|
830
778
|
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
|
|
831
779
|
nBytes = tx_sizes.vsize;
|
|
832
|
-
fee_needed =
|
|
780
|
+
fee_needed = GetMinFee(nBytes, GetAdjustedTime());
|
|
833
781
|
}
|
|
834
782
|
|
|
835
783
|
// The only time that fee_needed should be less than the amount available for fees (in change_and_fee - change_amount) is when
|
|
@@ -864,7 +812,7 @@ static bool CreateTransactionInternal(
|
|
|
864
812
|
}
|
|
865
813
|
|
|
866
814
|
// Error if this output is reduced to be below dust
|
|
867
|
-
if (
|
|
815
|
+
if (txout.nValue < MIN_TXOUT_AMOUNT) {
|
|
868
816
|
if (txout.nValue < 0) {
|
|
869
817
|
error = _("The transaction amount is too small to pay the fee");
|
|
870
818
|
} else {
|
|
@@ -899,11 +847,6 @@ static bool CreateTransactionInternal(
|
|
|
899
847
|
return false;
|
|
900
848
|
}
|
|
901
849
|
|
|
902
|
-
if (nFeeRet > wallet.m_default_max_tx_fee) {
|
|
903
|
-
error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
|
|
904
|
-
return false;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
850
|
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
|
|
908
851
|
// Lastly, ensure this tx will pass the mempool's chain limits
|
|
909
852
|
if (!wallet.chain().checkChainLimits(tx)) {
|
|
@@ -915,16 +858,8 @@ static bool CreateTransactionInternal(
|
|
|
915
858
|
// Before we return success, we assume any change key will be used to prevent
|
|
916
859
|
// accidental re-use.
|
|
917
860
|
reservedest.KeepDestination();
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
wallet.WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
|
|
921
|
-
nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
|
|
922
|
-
feeCalc.est.pass.start, feeCalc.est.pass.end,
|
|
923
|
-
(feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0,
|
|
924
|
-
feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool,
|
|
925
|
-
feeCalc.est.fail.start, feeCalc.est.fail.end,
|
|
926
|
-
(feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0,
|
|
927
|
-
feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
|
|
861
|
+
|
|
862
|
+
wallet.WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d\n", nFeeRet, nBytes, fee_needed);
|
|
928
863
|
return true;
|
|
929
864
|
}
|
|
930
865
|
|
|
@@ -936,7 +871,7 @@ bool CreateTransaction(
|
|
|
936
871
|
int& nChangePosInOut,
|
|
937
872
|
bilingual_str& error,
|
|
938
873
|
const CCoinControl& coin_control,
|
|
939
|
-
|
|
874
|
+
CAmount& fee_calc_out,
|
|
940
875
|
bool sign)
|
|
941
876
|
{
|
|
942
877
|
if (vecSend.empty()) {
|
|
@@ -998,7 +933,7 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
|
|
|
998
933
|
LOCK(wallet.cs_wallet);
|
|
999
934
|
|
|
1000
935
|
CTransactionRef tx_new;
|
|
1001
|
-
|
|
936
|
+
CAmount fee_calc_out;
|
|
1002
937
|
if (!CreateTransaction(wallet, vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
|
|
1003
938
|
return false;
|
|
1004
939
|
}
|
|
@@ -136,7 +136,7 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
|
|
|
136
136
|
* selected by SelectCoins(); Also create the change output, when needed
|
|
137
137
|
* @note passing nChangePosInOut as -1 will result in setting a random position
|
|
138
138
|
*/
|
|
139
|
-
bool CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control,
|
|
139
|
+
bool CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, CAmount& fee_calc_out, bool sign = true);
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
142
|
* Insert additional inputs into the transaction by
|
|
@@ -21,6 +21,10 @@ bool CWalletTx::InMempool() const
|
|
|
21
21
|
|
|
22
22
|
int64_t CWalletTx::GetTxTime() const
|
|
23
23
|
{
|
|
24
|
+
// peercoin: we still have the timestamp, so use it to avoid confusion
|
|
25
|
+
if (tx->nTime)
|
|
26
|
+
return tx->nTime;
|
|
27
|
+
|
|
24
28
|
int64_t n = nTimeSmart;
|
|
25
29
|
return n ? n : nTimeReceived;
|
|
26
30
|
}
|
|
@@ -145,10 +145,6 @@ public:
|
|
|
145
145
|
*
|
|
146
146
|
* "comment", "to" - comment strings provided to sendtoaddress,
|
|
147
147
|
* and sendmany wallet RPCs
|
|
148
|
-
* "replaces_txid" - txid (as HexStr) of transaction replaced by
|
|
149
|
-
* bumpfee on transaction created by bumpfee
|
|
150
|
-
* "replaced_by_txid" - txid (as HexStr) of transaction created by
|
|
151
|
-
* bumpfee on transaction replaced by bumpfee
|
|
152
148
|
* "from", "message" - obsolete fields that could be set in UI prior to
|
|
153
149
|
* 2011 (removed in commit 4d9b223)
|
|
154
150
|
*
|
|
@@ -297,6 +293,7 @@ public:
|
|
|
297
293
|
bool isConfirmed() const { return state<TxStateConfirmed>(); }
|
|
298
294
|
const uint256& GetHash() const { return tx->GetHash(); }
|
|
299
295
|
bool IsCoinBase() const { return tx->IsCoinBase(); }
|
|
296
|
+
bool IsCoinStake() const { return tx->IsCoinStake(); }
|
|
300
297
|
|
|
301
298
|
// Disable copying of CWalletTx objects to prevent bugs where instances get
|
|
302
299
|
// copied in and out of the mapWallet map, and fields are updated in the
|
|
@@ -9,14 +9,15 @@
|
|
|
9
9
|
#include <consensus/amount.h>
|
|
10
10
|
#include <consensus/consensus.h>
|
|
11
11
|
#include <consensus/validation.h>
|
|
12
|
+
#include <consensus/tx_verify.h>
|
|
12
13
|
#include <external_signer.h>
|
|
13
14
|
#include <fs.h>
|
|
15
|
+
#include <index/txindex.h>
|
|
14
16
|
#include <interfaces/chain.h>
|
|
15
17
|
#include <interfaces/wallet.h>
|
|
16
18
|
#include <key.h>
|
|
17
19
|
#include <key_io.h>
|
|
18
20
|
#include <outputtype.h>
|
|
19
|
-
#include <policy/fees.h>
|
|
20
21
|
#include <policy/policy.h>
|
|
21
22
|
#include <primitives/block.h>
|
|
22
23
|
#include <primitives/transaction.h>
|
|
@@ -24,18 +25,22 @@
|
|
|
24
25
|
#include <script/descriptor.h>
|
|
25
26
|
#include <script/script.h>
|
|
26
27
|
#include <script/signingprovider.h>
|
|
28
|
+
#include <timedata.h>
|
|
27
29
|
#include <txmempool.h>
|
|
28
30
|
#include <util/bip32.h>
|
|
29
31
|
#include <util/check.h>
|
|
30
32
|
#include <util/error.h>
|
|
31
|
-
#include <util/fees.h>
|
|
32
33
|
#include <util/moneystr.h>
|
|
33
|
-
#include <util/rbf.h>
|
|
34
34
|
#include <util/string.h>
|
|
35
35
|
#include <util/translation.h>
|
|
36
|
+
#include <validation.h>
|
|
37
|
+
#include <bignum.h>
|
|
38
|
+
#include <kernel.h>
|
|
39
|
+
#include <txdb.h>
|
|
36
40
|
#include <wallet/coincontrol.h>
|
|
37
41
|
#include <wallet/context.h>
|
|
38
|
-
#include <wallet/
|
|
42
|
+
#include <wallet/receive.h>
|
|
43
|
+
#include <wallet/spend.h>
|
|
39
44
|
#include <wallet/external_signer_scriptpubkeyman.h>
|
|
40
45
|
|
|
41
46
|
#include <univalue.h>
|
|
@@ -45,6 +50,7 @@
|
|
|
45
50
|
#include <optional>
|
|
46
51
|
|
|
47
52
|
#include <boost/algorithm/string/replace.hpp>
|
|
53
|
+
#include <boost/foreach.hpp>
|
|
48
54
|
|
|
49
55
|
using interfaces::FoundBlock;
|
|
50
56
|
|
|
@@ -370,6 +376,10 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
|
|
|
370
376
|
return wallet;
|
|
371
377
|
}
|
|
372
378
|
|
|
379
|
+
// peercoin: optional setting to unlock wallet for block minting only;
|
|
380
|
+
// serves to disable the trivial sendmoney when OS account compromised
|
|
381
|
+
bool fWalletUnlockMintOnly = false;
|
|
382
|
+
|
|
373
383
|
std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
|
|
374
384
|
{
|
|
375
385
|
DatabaseOptions options;
|
|
@@ -848,36 +858,29 @@ void CWallet::MarkDirty()
|
|
|
848
858
|
}
|
|
849
859
|
}
|
|
850
860
|
|
|
851
|
-
|
|
861
|
+
void CWallet::WalletUpdateSpent(const CTransactionRef &tx)
|
|
852
862
|
{
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
if (!batch.WriteTx(wtx)) {
|
|
874
|
-
WalletLogPrintf("%s: Updating batch tx %s failed\n", __func__, wtx.GetHash().ToString());
|
|
875
|
-
success = false;
|
|
863
|
+
// Anytime a signature is successfully verified, it's proof the outpoint is spent.
|
|
864
|
+
// Update the wallet spent flag if it doesn't know due to wallet.dat being
|
|
865
|
+
// restored from backup or the user making copies of wallet.dat.
|
|
866
|
+
{
|
|
867
|
+
LOCK(cs_wallet);
|
|
868
|
+
BOOST_FOREACH(const CTxIn& txin, tx->vin)
|
|
869
|
+
{
|
|
870
|
+
auto mi = mapWallet.find(txin.prevout.hash);
|
|
871
|
+
if (mi != mapWallet.end())
|
|
872
|
+
{
|
|
873
|
+
CWalletTx& wtx = (*mi).second;
|
|
874
|
+
if (txin.prevout.n >= wtx.tx->vout.size())
|
|
875
|
+
LogPrintf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str());
|
|
876
|
+
else if (IsMine(wtx.tx->vout[txin.prevout.n]))
|
|
877
|
+
{
|
|
878
|
+
LogPrintf("WalletUpdateSpent found spent coin %sppc %s\n", FormatMoney(CachedTxGetCredit(*this, wtx, ISMINE_SPENDABLE)).c_str(), wtx.GetHash().ToString().c_str());
|
|
879
|
+
NotifyTransactionChanged(txin.prevout.hash, CT_UPDATED);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
876
883
|
}
|
|
877
|
-
|
|
878
|
-
NotifyTransactionChanged(originalHash, CT_UPDATED);
|
|
879
|
-
|
|
880
|
-
return success;
|
|
881
884
|
}
|
|
882
885
|
|
|
883
886
|
void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)
|
|
@@ -998,6 +1001,9 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
|
|
|
998
1001
|
// Break debit/credit balance caches:
|
|
999
1002
|
wtx.MarkDirty();
|
|
1000
1003
|
|
|
1004
|
+
// since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
|
|
1005
|
+
WalletUpdateSpent(wtx.tx);
|
|
1006
|
+
|
|
1001
1007
|
// Notify UI of new or updated transaction
|
|
1002
1008
|
NotifyTransactionChanged(hash, fInsertedNew ? CT_NEW : CT_UPDATED);
|
|
1003
1009
|
|
|
@@ -1814,8 +1820,13 @@ void CWallet::ReacceptWalletTransactions()
|
|
|
1814
1820
|
|
|
1815
1821
|
int nDepth = GetTxDepthInMainChain(wtx);
|
|
1816
1822
|
|
|
1817
|
-
if (
|
|
1818
|
-
|
|
1823
|
+
if (nDepth == 0 && !wtx.isAbandoned()) {
|
|
1824
|
+
if (wtx.IsCoinBase() || wtx.IsCoinStake()) {
|
|
1825
|
+
LogPrintf("Abandoning wtx %s\n", wtx.GetHash().ToString());
|
|
1826
|
+
AbandonTransaction(wtxid);
|
|
1827
|
+
}
|
|
1828
|
+
else
|
|
1829
|
+
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
|
|
1819
1830
|
}
|
|
1820
1831
|
}
|
|
1821
1832
|
|
|
@@ -1850,7 +1861,7 @@ bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string
|
|
|
1850
1861
|
// If broadcast fails for any reason, trying to set wtx.m_state here would be incorrect.
|
|
1851
1862
|
// If transaction was previously in the mempool, it should be updated when
|
|
1852
1863
|
// TransactionRemovedFromMempool fires.
|
|
1853
|
-
bool ret = chain().broadcastTransaction(wtx.tx,
|
|
1864
|
+
bool ret = chain().broadcastTransaction(wtx.tx, relay, err_string);
|
|
1854
1865
|
if (ret) wtx.m_state = TxStateInMempool{};
|
|
1855
1866
|
return ret;
|
|
1856
1867
|
}
|
|
@@ -1939,7 +1950,7 @@ bool CWallet::SignTransaction(CMutableTransaction& tx) const
|
|
|
1939
1950
|
}
|
|
1940
1951
|
const CWalletTx& wtx = mi->second;
|
|
1941
1952
|
int prev_height = wtx.state<TxStateConfirmed>() ? wtx.state<TxStateConfirmed>()->confirmed_block_height : 0;
|
|
1942
|
-
coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], prev_height, wtx.IsCoinBase());
|
|
1953
|
+
coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], prev_height, wtx.IsCoinBase(), wtx.IsCoinStake(), wtx.nTimeSmart);
|
|
1943
1954
|
}
|
|
1944
1955
|
std::map<int, bilingual_str> input_errors;
|
|
1945
1956
|
return SignTransaction(tx, coins, SIGHASH_DEFAULT, input_errors);
|
|
@@ -2790,116 +2801,12 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
|
|
|
2790
2801
|
walletInstance->m_default_change_type = parsed.value();
|
|
2791
2802
|
}
|
|
2792
2803
|
|
|
2793
|
-
if (args.IsArgSet("-mintxfee")) {
|
|
2794
|
-
std::optional<CAmount> min_tx_fee = ParseMoney(args.GetArg("-mintxfee", ""));
|
|
2795
|
-
if (!min_tx_fee || min_tx_fee.value() == 0) {
|
|
2796
|
-
error = AmountErrMsg("mintxfee", args.GetArg("-mintxfee", ""));
|
|
2797
|
-
return nullptr;
|
|
2798
|
-
} else if (min_tx_fee.value() > HIGH_TX_FEE_PER_KB) {
|
|
2799
|
-
warnings.push_back(AmountHighWarn("-mintxfee") + Untranslated(" ") +
|
|
2800
|
-
_("This is the minimum transaction fee you pay on every transaction."));
|
|
2801
|
-
}
|
|
2802
|
-
|
|
2803
|
-
walletInstance->m_min_fee = CFeeRate{min_tx_fee.value()};
|
|
2804
|
-
}
|
|
2805
|
-
|
|
2806
|
-
if (args.IsArgSet("-maxapsfee")) {
|
|
2807
|
-
const std::string max_aps_fee{args.GetArg("-maxapsfee", "")};
|
|
2808
|
-
if (max_aps_fee == "-1") {
|
|
2809
|
-
walletInstance->m_max_aps_fee = -1;
|
|
2810
|
-
} else if (std::optional<CAmount> max_fee = ParseMoney(max_aps_fee)) {
|
|
2811
|
-
if (max_fee.value() > HIGH_APS_FEE) {
|
|
2812
|
-
warnings.push_back(AmountHighWarn("-maxapsfee") + Untranslated(" ") +
|
|
2813
|
-
_("This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection."));
|
|
2814
|
-
}
|
|
2815
|
-
walletInstance->m_max_aps_fee = max_fee.value();
|
|
2816
|
-
} else {
|
|
2817
|
-
error = AmountErrMsg("maxapsfee", max_aps_fee);
|
|
2818
|
-
return nullptr;
|
|
2819
|
-
}
|
|
2820
|
-
}
|
|
2821
|
-
|
|
2822
|
-
if (args.IsArgSet("-fallbackfee")) {
|
|
2823
|
-
std::optional<CAmount> fallback_fee = ParseMoney(args.GetArg("-fallbackfee", ""));
|
|
2824
|
-
if (!fallback_fee) {
|
|
2825
|
-
error = strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), args.GetArg("-fallbackfee", ""));
|
|
2826
|
-
return nullptr;
|
|
2827
|
-
} else if (fallback_fee.value() > HIGH_TX_FEE_PER_KB) {
|
|
2828
|
-
warnings.push_back(AmountHighWarn("-fallbackfee") + Untranslated(" ") +
|
|
2829
|
-
_("This is the transaction fee you may pay when fee estimates are not available."));
|
|
2830
|
-
}
|
|
2831
|
-
walletInstance->m_fallback_fee = CFeeRate{fallback_fee.value()};
|
|
2832
|
-
}
|
|
2833
|
-
|
|
2834
|
-
// Disable fallback fee in case value was set to 0, enable if non-null value
|
|
2835
|
-
walletInstance->m_allow_fallback_fee = walletInstance->m_fallback_fee.GetFeePerK() != 0;
|
|
2836
|
-
|
|
2837
|
-
if (args.IsArgSet("-discardfee")) {
|
|
2838
|
-
std::optional<CAmount> discard_fee = ParseMoney(args.GetArg("-discardfee", ""));
|
|
2839
|
-
if (!discard_fee) {
|
|
2840
|
-
error = strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), args.GetArg("-discardfee", ""));
|
|
2841
|
-
return nullptr;
|
|
2842
|
-
} else if (discard_fee.value() > HIGH_TX_FEE_PER_KB) {
|
|
2843
|
-
warnings.push_back(AmountHighWarn("-discardfee") + Untranslated(" ") +
|
|
2844
|
-
_("This is the transaction fee you may discard if change is smaller than dust at this level"));
|
|
2845
|
-
}
|
|
2846
|
-
walletInstance->m_discard_rate = CFeeRate{discard_fee.value()};
|
|
2847
|
-
}
|
|
2848
|
-
|
|
2849
|
-
if (args.IsArgSet("-paytxfee")) {
|
|
2850
|
-
std::optional<CAmount> pay_tx_fee = ParseMoney(args.GetArg("-paytxfee", ""));
|
|
2851
|
-
if (!pay_tx_fee) {
|
|
2852
|
-
error = AmountErrMsg("paytxfee", args.GetArg("-paytxfee", ""));
|
|
2853
|
-
return nullptr;
|
|
2854
|
-
} else if (pay_tx_fee.value() > HIGH_TX_FEE_PER_KB) {
|
|
2855
|
-
warnings.push_back(AmountHighWarn("-paytxfee") + Untranslated(" ") +
|
|
2856
|
-
_("This is the transaction fee you will pay if you send a transaction."));
|
|
2857
|
-
}
|
|
2858
|
-
|
|
2859
|
-
walletInstance->m_pay_tx_fee = CFeeRate{pay_tx_fee.value(), 1000};
|
|
2860
|
-
|
|
2861
|
-
if (chain && walletInstance->m_pay_tx_fee < chain->relayMinFee()) {
|
|
2862
|
-
error = strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
|
|
2863
|
-
args.GetArg("-paytxfee", ""), chain->relayMinFee().ToString());
|
|
2864
|
-
return nullptr;
|
|
2865
|
-
}
|
|
2866
|
-
}
|
|
2867
|
-
|
|
2868
|
-
if (args.IsArgSet("-maxtxfee")) {
|
|
2869
|
-
std::optional<CAmount> max_fee = ParseMoney(args.GetArg("-maxtxfee", ""));
|
|
2870
|
-
if (!max_fee) {
|
|
2871
|
-
error = AmountErrMsg("maxtxfee", args.GetArg("-maxtxfee", ""));
|
|
2872
|
-
return nullptr;
|
|
2873
|
-
} else if (max_fee.value() > HIGH_MAX_TX_FEE) {
|
|
2874
|
-
warnings.push_back(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
|
|
2875
|
-
}
|
|
2876
|
-
|
|
2877
|
-
if (chain && CFeeRate{max_fee.value(), 1000} < chain->relayMinFee()) {
|
|
2878
|
-
error = strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
|
|
2879
|
-
args.GetArg("-maxtxfee", ""), chain->relayMinFee().ToString());
|
|
2880
|
-
return nullptr;
|
|
2881
|
-
}
|
|
2882
|
-
|
|
2883
|
-
walletInstance->m_default_max_tx_fee = max_fee.value();
|
|
2884
|
-
}
|
|
2885
|
-
|
|
2886
|
-
if (args.IsArgSet("-consolidatefeerate")) {
|
|
2887
|
-
if (std::optional<CAmount> consolidate_feerate = ParseMoney(args.GetArg("-consolidatefeerate", ""))) {
|
|
2888
|
-
walletInstance->m_consolidate_feerate = CFeeRate(*consolidate_feerate);
|
|
2889
|
-
} else {
|
|
2890
|
-
error = AmountErrMsg("consolidatefeerate", args.GetArg("-consolidatefeerate", ""));
|
|
2891
|
-
return nullptr;
|
|
2892
|
-
}
|
|
2893
|
-
}
|
|
2894
|
-
|
|
2895
|
-
if (chain && chain->relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) {
|
|
2896
|
-
warnings.push_back(AmountHighWarn("-minrelaytxfee") + Untranslated(" ") +
|
|
2897
|
-
_("The wallet will avoid paying less than the minimum relay fee."));
|
|
2898
|
-
}
|
|
2899
|
-
|
|
2900
|
-
walletInstance->m_confirm_target = args.GetIntArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
|
|
2901
2804
|
walletInstance->m_spend_zero_conf_change = args.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
|
|
2902
|
-
walletInstance->
|
|
2805
|
+
walletInstance->m_split_coins = args.GetBoolArg("-splitcoins", DEFAULT_SPLIT_COINS);
|
|
2806
|
+
walletInstance->WalletLogPrintf("Wallet will%s split coins during minting\n", walletInstance->m_split_coins? "" : " not");
|
|
2807
|
+
|
|
2808
|
+
walletInstance->m_check_github = args.GetBoolArg("-checkgithub", DEFAULT_CHECK_GITHUB);
|
|
2809
|
+
walletInstance->WalletLogPrintf("Wallet will%s check github for newer version on startup\n", walletInstance->m_check_github? "" : " not");
|
|
2903
2810
|
|
|
2904
2811
|
walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", GetTimeMillis() - nStart);
|
|
2905
2812
|
|
|
@@ -2962,24 +2869,6 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
|
|
|
2962
2869
|
|
|
2963
2870
|
if (tip_height && *tip_height != rescan_height)
|
|
2964
2871
|
{
|
|
2965
|
-
if (chain.havePruned()) {
|
|
2966
|
-
int block_height = *tip_height;
|
|
2967
|
-
while (block_height > 0 && chain.haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {
|
|
2968
|
-
--block_height;
|
|
2969
|
-
}
|
|
2970
|
-
|
|
2971
|
-
if (rescan_height != block_height) {
|
|
2972
|
-
// We can't rescan beyond non-pruned blocks, stop and throw an error.
|
|
2973
|
-
// This might happen if a user uses an old wallet within a pruned node
|
|
2974
|
-
// or if they ran -disablewallet for a longer time, then decided to re-enable
|
|
2975
|
-
// Exit early and print an error.
|
|
2976
|
-
// If a block is pruned after this check, we will load the wallet,
|
|
2977
|
-
// but fail the rescan with a generic error.
|
|
2978
|
-
error = _("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)");
|
|
2979
|
-
return false;
|
|
2980
|
-
}
|
|
2981
|
-
}
|
|
2982
|
-
|
|
2983
2872
|
chain.initMessage(_("Rescanning…").translated);
|
|
2984
2873
|
walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height);
|
|
2985
2874
|
|
|
@@ -3097,11 +2986,12 @@ int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const
|
|
|
3097
2986
|
|
|
3098
2987
|
int CWallet::GetTxBlocksToMaturity(const CWalletTx& wtx) const
|
|
3099
2988
|
{
|
|
3100
|
-
if (!wtx.IsCoinBase())
|
|
2989
|
+
if (!(wtx.IsCoinBase() || wtx.IsCoinStake()))
|
|
3101
2990
|
return 0;
|
|
3102
2991
|
int chain_depth = GetTxDepthInMainChain(wtx);
|
|
3103
|
-
|
|
3104
|
-
|
|
2992
|
+
if (!wtx.IsCoinStake())
|
|
2993
|
+
assert(chain_depth >= 0); // coinbase tx should not be conflicted
|
|
2994
|
+
return std::max(0, (Params().GetConsensus().nCoinbaseMaturity+1) - chain_depth);
|
|
3105
2995
|
}
|
|
3106
2996
|
|
|
3107
2997
|
bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const
|
|
@@ -3272,6 +3162,277 @@ void CWallet::ConnectScriptPubKeyManNotifiers()
|
|
|
3272
3162
|
}
|
|
3273
3163
|
}
|
|
3274
3164
|
|
|
3165
|
+
// peercoin: create coin stake transaction
|
|
3166
|
+
typedef std::vector<unsigned char> valtype;
|
|
3167
|
+
bool CWallet::CreateCoinStake(ChainstateManager& chainman, const CWallet* pwallet, unsigned int nBits, int64_t nSearchInterval, CMutableTransaction& txNew)
|
|
3168
|
+
{
|
|
3169
|
+
bool bDebug = (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printcoinstake", false));
|
|
3170
|
+
// if there are pre signed coinstakes, we'll use them for minting
|
|
3171
|
+
if (m_coinstakes.size()) {
|
|
3172
|
+
|
|
3173
|
+
uint32_t nTime = GetTime();
|
|
3174
|
+
if (bDebug)
|
|
3175
|
+
LogPrintf("there are imported coinstakes, time is %d, nSearchInterval %d\n", nTime, nSearchInterval);
|
|
3176
|
+
|
|
3177
|
+
for (const auto& [timestamp, txn] : m_coinstakes) {
|
|
3178
|
+
// check timestamp
|
|
3179
|
+
if (nTime > timestamp) {
|
|
3180
|
+
if (nTime - nSearchInterval <= timestamp) {
|
|
3181
|
+
if (bDebug)
|
|
3182
|
+
LogPrintf("timestamp within nSearchInterval, using coinstake\n");
|
|
3183
|
+
CMutableTransaction presigned(*txn);
|
|
3184
|
+
txNew = presigned;
|
|
3185
|
+
return true;
|
|
3186
|
+
}
|
|
3187
|
+
else {
|
|
3188
|
+
if (bDebug)
|
|
3189
|
+
LogPrintf("timestamp too old, removing coinstake\n");
|
|
3190
|
+
m_coinstakes.erase(timestamp);
|
|
3191
|
+
break;
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
// The following split & combine thresholds are important to security
|
|
3198
|
+
// Should not be adjusted if you don't understand the consequences
|
|
3199
|
+
static unsigned int nStakeSplitAge = (60 * 60 * 24 * 90);
|
|
3200
|
+
int64_t nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(chainman.ActiveChain().Tip(), false)->nBits, txNew.nTime) / 3;
|
|
3201
|
+
|
|
3202
|
+
CBigNum bnTargetPerCoinDay;
|
|
3203
|
+
bnTargetPerCoinDay.SetCompact(nBits);
|
|
3204
|
+
|
|
3205
|
+
// Transaction index is required to get to block header
|
|
3206
|
+
if (!g_txindex)
|
|
3207
|
+
return error("CreateCoinStake : transaction index unavailable");
|
|
3208
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
3209
|
+
|
|
3210
|
+
LOCK2(cs_main, pwallet->cs_wallet);
|
|
3211
|
+
txNew.vin.clear();
|
|
3212
|
+
txNew.vout.clear();
|
|
3213
|
+
// Mark coin stake transaction
|
|
3214
|
+
CScript scriptEmpty;
|
|
3215
|
+
scriptEmpty.clear();
|
|
3216
|
+
txNew.vout.push_back(CTxOut(0, scriptEmpty));
|
|
3217
|
+
// Choose coins to use
|
|
3218
|
+
CAmount nBalance = GetBalance(*this).m_mine_trusted;
|
|
3219
|
+
std::optional<CAmount> nReserveBalance = ParseMoney(gArgs.GetArg("-reservebalance", ""));
|
|
3220
|
+
if (gArgs.IsArgSet("-reservebalance") && !nReserveBalance)
|
|
3221
|
+
return error("CreateCoinStake : invalid reserve balance amount");
|
|
3222
|
+
if (nBalance <= nReserveBalance)
|
|
3223
|
+
return false;
|
|
3224
|
+
std::vector<CTransactionRef> vwtxPrev;
|
|
3225
|
+
CAmount nValueIn = 0;
|
|
3226
|
+
std::vector<COutput> vAvailableCoins;
|
|
3227
|
+
CCoinControl temp;
|
|
3228
|
+
CoinSelectionParams coin_selection_params;
|
|
3229
|
+
coin_selection_params.m_subtract_fee_outputs = true;
|
|
3230
|
+
|
|
3231
|
+
bool bnb_used;
|
|
3232
|
+
AvailableCoins(*pwallet, vAvailableCoins, &temp);
|
|
3233
|
+
|
|
3234
|
+
CAmount nAllowedBalance = nBalance;
|
|
3235
|
+
if (nReserveBalance) nAllowedBalance -= nReserveBalance.value();
|
|
3236
|
+
|
|
3237
|
+
std::optional<SelectionResult> result = SelectCoins(*pwallet, vAvailableCoins, nAllowedBalance, temp, coin_selection_params);
|
|
3238
|
+
|
|
3239
|
+
if (!result)
|
|
3240
|
+
return false;
|
|
3241
|
+
|
|
3242
|
+
CAmount nCredit = 0;
|
|
3243
|
+
CScript scriptPubKeyKernel;
|
|
3244
|
+
|
|
3245
|
+
for (const auto& pcoin : result->GetInputSet())
|
|
3246
|
+
{
|
|
3247
|
+
CDiskTxPos postx;
|
|
3248
|
+
if (!g_txindex->FindTxPosition(pcoin.outpoint.hash, postx))
|
|
3249
|
+
continue;
|
|
3250
|
+
|
|
3251
|
+
CBlockHeader header;
|
|
3252
|
+
CTransactionRef tx;
|
|
3253
|
+
auto it = g_txindex->cachedTxs.find(pcoin.outpoint.hash);
|
|
3254
|
+
if (it != g_txindex->cachedTxs.end()) {
|
|
3255
|
+
header = it->second.first;
|
|
3256
|
+
tx = it->second.second;
|
|
3257
|
+
} else {
|
|
3258
|
+
try {
|
|
3259
|
+
// Read block header
|
|
3260
|
+
CAutoFile file(node::OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
|
3261
|
+
file >> header;
|
|
3262
|
+
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
|
|
3263
|
+
file >> tx;
|
|
3264
|
+
g_txindex->cachedTxs[pcoin.outpoint.hash]=std::pair(header,tx);
|
|
3265
|
+
} catch (std::exception &e) {
|
|
3266
|
+
return error("%s() : deserialize or I/O error in CreateCoinStake()", __PRETTY_FUNCTION__);
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3270
|
+
static int nMaxStakeSearchInterval = 60;
|
|
3271
|
+
if (header.GetBlockTime() + params.nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval)
|
|
3272
|
+
continue; // only count coins meeting min age requirement
|
|
3273
|
+
|
|
3274
|
+
bool fKernelFound = false;
|
|
3275
|
+
for (unsigned int n=0; n<std::min(nSearchInterval,(int64_t)nMaxStakeSearchInterval) && !fKernelFound; n++)
|
|
3276
|
+
{
|
|
3277
|
+
// Search backward in time from the given txNew timestamp
|
|
3278
|
+
// Search nSearchInterval seconds back up to nMaxStakeSearchInterval
|
|
3279
|
+
uint256 hashProofOfStake = uint256();
|
|
3280
|
+
COutPoint prevoutStake = pcoin.outpoint;
|
|
3281
|
+
if (CheckStakeKernelHash(nBits, chainman.ActiveChain().Tip(), header, postx.nTxOffset + CBlockHeader::NORMAL_SERIALIZE_SIZE, tx, prevoutStake, txNew.nTime - n, hashProofOfStake, false, chainman.ActiveChainstate()))
|
|
3282
|
+
{
|
|
3283
|
+
// Found a kernel
|
|
3284
|
+
if (bDebug)
|
|
3285
|
+
LogPrintf("CreateCoinStake : kernel found\n");
|
|
3286
|
+
std::vector<valtype> vSolutions;
|
|
3287
|
+
TxoutType whichType;
|
|
3288
|
+
CScript scriptPubKeyOut;
|
|
3289
|
+
scriptPubKeyKernel = pcoin.txout.scriptPubKey;
|
|
3290
|
+
whichType = Solver(scriptPubKeyKernel, vSolutions);
|
|
3291
|
+
|
|
3292
|
+
if (bDebug)
|
|
3293
|
+
LogPrintf("CreateCoinStake : parsed kernel type=%s\n", GetTxnOutputType(whichType));
|
|
3294
|
+
if (whichType != TxoutType::PUBKEY && whichType != TxoutType::PUBKEYHASH && whichType != TxoutType::WITNESS_V0_KEYHASH)
|
|
3295
|
+
{
|
|
3296
|
+
if (bDebug)
|
|
3297
|
+
LogPrintf("CreateCoinStake : no support for kernel type=%s\n", GetTxnOutputType(whichType));
|
|
3298
|
+
break; // only support pay to public key and pay to address and pay to witness keyhash
|
|
3299
|
+
}
|
|
3300
|
+
if (whichType == TxoutType::PUBKEYHASH || whichType == TxoutType::WITNESS_V0_KEYHASH) // pay to address type or witness keyhash
|
|
3301
|
+
{
|
|
3302
|
+
// convert to pay to public key type
|
|
3303
|
+
CKey key;
|
|
3304
|
+
if (!pwallet->GetLegacyScriptPubKeyMan()->GetKey(CKeyID(uint160(vSolutions[0])), key))
|
|
3305
|
+
{
|
|
3306
|
+
if (bDebug)
|
|
3307
|
+
LogPrintf("CreateCoinStake : failed to get key for kernel type=%s\n", GetTxnOutputType(whichType));
|
|
3308
|
+
break; // unable to find corresponding public key
|
|
3309
|
+
}
|
|
3310
|
+
scriptPubKeyOut << ToByteVector(key.GetPubKey()) << OP_CHECKSIG;
|
|
3311
|
+
}
|
|
3312
|
+
else
|
|
3313
|
+
scriptPubKeyOut = scriptPubKeyKernel;
|
|
3314
|
+
|
|
3315
|
+
txNew.nTime -= n;
|
|
3316
|
+
txNew.vin.push_back(CTxIn(pcoin.outpoint.hash, pcoin.outpoint.n));
|
|
3317
|
+
nCredit += pcoin.txout.nValue;
|
|
3318
|
+
vwtxPrev.push_back(tx);
|
|
3319
|
+
txNew.vout.push_back(CTxOut(0, scriptPubKeyOut));
|
|
3320
|
+
if ((header.GetBlockTime() + nStakeSplitAge > txNew.nTime) && pwallet->m_split_coins)
|
|
3321
|
+
txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); //split stake
|
|
3322
|
+
if (bDebug)
|
|
3323
|
+
LogPrintf("CreateCoinStake : added kernel type=%s\n", GetTxnOutputType(whichType));
|
|
3324
|
+
fKernelFound = true;
|
|
3325
|
+
break;
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
if (fKernelFound)
|
|
3329
|
+
break; // if kernel is found stop searching
|
|
3330
|
+
}
|
|
3331
|
+
if (nCredit == 0 || nCredit > nAllowedBalance)
|
|
3332
|
+
return false;
|
|
3333
|
+
for (const auto& pcoin : result->GetInputSet())
|
|
3334
|
+
{
|
|
3335
|
+
CDiskTxPos postx;
|
|
3336
|
+
if (!g_txindex->FindTxPosition(pcoin.outpoint.hash, postx))
|
|
3337
|
+
continue;
|
|
3338
|
+
|
|
3339
|
+
// Read block header
|
|
3340
|
+
CAutoFile file(node::OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
|
3341
|
+
CBlockHeader header;
|
|
3342
|
+
CTransactionRef tx;
|
|
3343
|
+
try {
|
|
3344
|
+
file >> header;
|
|
3345
|
+
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
|
|
3346
|
+
file >> tx;
|
|
3347
|
+
} catch (std::exception &e) {
|
|
3348
|
+
return error("%s() : deserialize or I/O error in CreateCoinStake()", __PRETTY_FUNCTION__);
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
|
|
3352
|
+
// Attempt to add more inputs
|
|
3353
|
+
// Only add coins of the same key/address as kernel
|
|
3354
|
+
if (txNew.vout.size() == 2 && ((pcoin.txout.scriptPubKey == scriptPubKeyKernel || pcoin.txout.scriptPubKey == txNew.vout[1].scriptPubKey))
|
|
3355
|
+
&& pcoin.outpoint.hash != txNew.vin[0].prevout.hash)
|
|
3356
|
+
{
|
|
3357
|
+
// Stop adding more inputs if already too many inputs
|
|
3358
|
+
if (txNew.vin.size() >= 100)
|
|
3359
|
+
break;
|
|
3360
|
+
// Stop adding more inputs if value is already pretty significant
|
|
3361
|
+
if (nCredit > nCombineThreshold)
|
|
3362
|
+
break;
|
|
3363
|
+
// Stop adding inputs if reached reserve limit
|
|
3364
|
+
if (nCredit + pcoin.txout.nValue > nBalance - nReserveBalance.value())
|
|
3365
|
+
break;
|
|
3366
|
+
// Do not add additional significant input
|
|
3367
|
+
if (pcoin.txout.nValue > nCombineThreshold)
|
|
3368
|
+
continue;
|
|
3369
|
+
// Do not add input that is still too young
|
|
3370
|
+
if (tx->nTime + params.nStakeMaxAge > txNew.nTime)
|
|
3371
|
+
continue;
|
|
3372
|
+
txNew.vin.push_back(CTxIn(pcoin.outpoint.hash, pcoin.outpoint.n));
|
|
3373
|
+
nCredit += pcoin.txout.nValue;
|
|
3374
|
+
vwtxPrev.push_back(tx);
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
// Calculate coin age reward
|
|
3378
|
+
{
|
|
3379
|
+
uint64_t nCoinAge;
|
|
3380
|
+
CCoinsViewCache view(&chainman.ActiveChainstate().CoinsTip());
|
|
3381
|
+
if (!GetCoinAge((const CTransaction)txNew, view, nCoinAge, txNew.nTime, true))
|
|
3382
|
+
return error("CreateCoinStake : failed to calculate coin age");
|
|
3383
|
+
|
|
3384
|
+
CAmount nReward = GetProofOfStakeReward(nCoinAge, txNew.nTime, chainman.ActiveChain().Tip()->nMoneySupply);
|
|
3385
|
+
// Refuse to create mint that has zero or negative reward
|
|
3386
|
+
if(nReward <= 0) {
|
|
3387
|
+
return false;
|
|
3388
|
+
}
|
|
3389
|
+
nCredit += nReward;
|
|
3390
|
+
}
|
|
3391
|
+
|
|
3392
|
+
CAmount nMinFee = 0;
|
|
3393
|
+
CAmount nMinFeeBase = (IsProtocolV07(txNew.nTime) ? MIN_TX_FEE : MIN_TX_FEE_PREV7);
|
|
3394
|
+
while(true)
|
|
3395
|
+
{
|
|
3396
|
+
// Set output amount
|
|
3397
|
+
if (txNew.vout.size() == 3)
|
|
3398
|
+
{
|
|
3399
|
+
txNew.vout[1].nValue = ((nCredit - nMinFee) / 2 / nMinFeeBase) * nMinFeeBase;
|
|
3400
|
+
txNew.vout[2].nValue = nCredit - nMinFee - txNew.vout[1].nValue;
|
|
3401
|
+
}
|
|
3402
|
+
else
|
|
3403
|
+
txNew.vout[1].nValue = nCredit - nMinFee;
|
|
3404
|
+
|
|
3405
|
+
// Sign
|
|
3406
|
+
int nIn = 0;
|
|
3407
|
+
for (const auto& pcoin : vwtxPrev)
|
|
3408
|
+
{
|
|
3409
|
+
if (!SignSignature(*pwallet->GetLegacyScriptPubKeyMan(), *pcoin, txNew, nIn++, SIGHASH_ALL))
|
|
3410
|
+
return error("CreateCoinStake : failed to sign coinstake");
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
// Limit size
|
|
3414
|
+
unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
|
|
3415
|
+
if (nBytes >= 1000000/5)
|
|
3416
|
+
return error("CreateCoinStake : exceeded coinstake size limit");
|
|
3417
|
+
|
|
3418
|
+
// Check enough fee is paid
|
|
3419
|
+
if (nMinFee < GetMinFee(CTransaction(txNew), txNew.nTime) - nMinFeeBase)
|
|
3420
|
+
{
|
|
3421
|
+
nMinFee = GetMinFee(CTransaction(txNew), txNew.nTime) - nMinFeeBase;
|
|
3422
|
+
continue; // try signing again
|
|
3423
|
+
}
|
|
3424
|
+
else
|
|
3425
|
+
{
|
|
3426
|
+
if (bDebug)
|
|
3427
|
+
LogPrintf("CreateCoinStake : fee for coinstake %s\n", FormatMoney(nMinFee).c_str());
|
|
3428
|
+
break;
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
|
|
3432
|
+
// Successfully generated coinstake
|
|
3433
|
+
return true;
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3275
3436
|
void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc)
|
|
3276
3437
|
{
|
|
3277
3438
|
if (IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
#define BITCOIN_WALLET_WALLET_H
|
|
8
8
|
|
|
9
9
|
#include <consensus/amount.h>
|
|
10
|
+
#include <consensus/tx_verify.h>
|
|
10
11
|
#include <fs.h>
|
|
11
12
|
#include <interfaces/chain.h>
|
|
12
13
|
#include <interfaces/handler.h>
|
|
13
14
|
#include <outputtype.h>
|
|
14
|
-
#include <policy/feerate.h>
|
|
15
15
|
#include <psbt.h>
|
|
16
16
|
#include <tinyformat.h>
|
|
17
17
|
#include <util/message.h>
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
#include <util/string.h>
|
|
20
20
|
#include <util/system.h>
|
|
21
21
|
#include <util/ui_change_type.h>
|
|
22
|
+
#include <validation.h>
|
|
22
23
|
#include <validationinterface.h>
|
|
23
24
|
#include <wallet/coinselection.h>
|
|
24
25
|
#include <wallet/crypter.h>
|
|
@@ -46,7 +47,6 @@ using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wall
|
|
|
46
47
|
|
|
47
48
|
class CScript;
|
|
48
49
|
enum class FeeEstimateMode;
|
|
49
|
-
struct FeeCalculation;
|
|
50
50
|
struct bilingual_str;
|
|
51
51
|
|
|
52
52
|
namespace wallet {
|
|
@@ -71,14 +71,9 @@ std::unique_ptr<interfaces::Handler> HandleLoadWallet(WalletContext& context, Lo
|
|
|
71
71
|
void NotifyWalletLoaded(WalletContext& context, const std::shared_ptr<CWallet>& wallet);
|
|
72
72
|
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
static const CAmount DEFAULT_FALLBACK_FEE = 0;
|
|
78
|
-
//! -discardfee default
|
|
79
|
-
static const CAmount DEFAULT_DISCARD_FEE = 10000;
|
|
80
|
-
//! -mintxfee default
|
|
81
|
-
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
|
|
74
|
+
extern bool fWalletUnlockMintOnly;
|
|
75
|
+
|
|
76
|
+
static const CAmount MIN_CHANGE = MIN_TXOUT_AMOUNT;
|
|
82
77
|
//! -consolidatefeerate default
|
|
83
78
|
static const CAmount DEFAULT_CONSOLIDATE_FEERATE{10000}; // 10 sat/vbyte
|
|
84
79
|
/**
|
|
@@ -91,24 +86,18 @@ static const CAmount DEFAULT_CONSOLIDATE_FEERATE{10000}; // 10 sat/vbyte
|
|
|
91
86
|
static const CAmount DEFAULT_MAX_AVOIDPARTIALSPEND_FEE = 0;
|
|
92
87
|
//! discourage APS fee higher than this amount
|
|
93
88
|
constexpr CAmount HIGH_APS_FEE{COIN / 10000};
|
|
94
|
-
//! minimum recommended increment for BIP 125 replacement txs
|
|
95
|
-
static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000;
|
|
96
89
|
//! Default for -spendzeroconfchange
|
|
97
90
|
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
|
|
91
|
+
//! Default for -splitcoins
|
|
92
|
+
static const bool DEFAULT_SPLIT_COINS = true;
|
|
93
|
+
//! Default for -checkgithub
|
|
94
|
+
static const bool DEFAULT_CHECK_GITHUB = true;
|
|
98
95
|
//! Default for -walletrejectlongchains
|
|
99
96
|
static const bool DEFAULT_WALLET_REJECT_LONG_CHAINS = false;
|
|
100
97
|
//! -txconfirmtarget default
|
|
101
98
|
static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6;
|
|
102
|
-
//! -walletrbf default
|
|
103
|
-
static const bool DEFAULT_WALLET_RBF = false;
|
|
104
99
|
static const bool DEFAULT_WALLETBROADCAST = true;
|
|
105
100
|
static const bool DEFAULT_DISABLE_WALLET = false;
|
|
106
|
-
//! -maxtxfee default
|
|
107
|
-
constexpr CAmount DEFAULT_TRANSACTION_MAXFEE{COIN / 10};
|
|
108
|
-
//! Discourage users to set fees higher than this amount (in satoshis) per kB
|
|
109
|
-
constexpr CAmount HIGH_TX_FEE_PER_KB{COIN / 100};
|
|
110
|
-
//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
|
|
111
|
-
constexpr CAmount HIGH_MAX_TX_FEE{100 * HIGH_TX_FEE_PER_KB};
|
|
112
101
|
//! Pre-calculated constants for input size estimation in *virtual size*
|
|
113
102
|
static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
|
|
114
103
|
|
|
@@ -118,7 +107,7 @@ class CWalletTx;
|
|
|
118
107
|
class ReserveDestination;
|
|
119
108
|
|
|
120
109
|
//! Default for -addresstype
|
|
121
|
-
constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::
|
|
110
|
+
constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::LEGACY};
|
|
122
111
|
|
|
123
112
|
static constexpr uint64_t KNOWN_WALLET_FLAGS =
|
|
124
113
|
WALLET_FLAG_AVOID_REUSE
|
|
@@ -401,6 +390,8 @@ public:
|
|
|
401
390
|
std::map<CTxDestination, CAddressBookData> m_address_book GUARDED_BY(cs_wallet);
|
|
402
391
|
const CAddressBookData* FindAddressBookEntry(const CTxDestination&, bool allow_change = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
|
403
392
|
|
|
393
|
+
std::map<uint32_t, CTransactionRef> m_coinstakes;
|
|
394
|
+
|
|
404
395
|
/** Set of Coins owned by this wallet that we won't try to spend from. A
|
|
405
396
|
* Coin may be locked if it has already been used to fund a transaction
|
|
406
397
|
* that hasn't confirmed yet. We wouldn't consider the Coin spent already,
|
|
@@ -503,6 +494,7 @@ public:
|
|
|
503
494
|
int64_t IncOrderPosNext(WalletBatch *batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
|
504
495
|
DBErrors ReorderTransactions();
|
|
505
496
|
|
|
497
|
+
void WalletUpdateSpent(const CTransactionRef& tx);
|
|
506
498
|
void MarkDirty();
|
|
507
499
|
|
|
508
500
|
//! Callback for updating transaction metadata in mapWallet.
|
|
@@ -582,6 +574,7 @@ public:
|
|
|
582
574
|
* @param[in] orderForm BIP 70 / BIP 21 order form details to be set on the transaction.
|
|
583
575
|
*/
|
|
584
576
|
void CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm);
|
|
577
|
+
bool CreateCoinStake(ChainstateManager& chainman, const CWallet* pwallet, unsigned int nBits, int64_t nSearchInterval, CMutableTransaction &txNew);
|
|
585
578
|
|
|
586
579
|
/** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
|
|
587
580
|
bool SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, bool relay) const;
|
|
@@ -599,30 +592,11 @@ public:
|
|
|
599
592
|
bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
|
600
593
|
bool ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
|
601
594
|
|
|
602
|
-
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
|
|
603
|
-
unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
|
|
604
595
|
/** Allow Coin Selection to pick unconfirmed UTXOs that were sent from our own wallet if it
|
|
605
596
|
* cannot fund the transaction otherwise. */
|
|
606
597
|
bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE};
|
|
607
|
-
bool
|
|
608
|
-
bool
|
|
609
|
-
CFeeRate m_min_fee{DEFAULT_TRANSACTION_MINFEE}; //!< Override with -mintxfee
|
|
610
|
-
/**
|
|
611
|
-
* If fee estimation does not have enough data to provide estimates, use this fee instead.
|
|
612
|
-
* Has no effect if not using fee estimation
|
|
613
|
-
* Override with -fallbackfee
|
|
614
|
-
*/
|
|
615
|
-
CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE};
|
|
616
|
-
|
|
617
|
-
/** If the cost to spend a change output at this feerate is greater than the value of the
|
|
618
|
-
* output itself, just drop it to fees. */
|
|
619
|
-
CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE};
|
|
620
|
-
|
|
621
|
-
/** When the actual feerate is less than the consolidate feerate, we will tend to make transactions which
|
|
622
|
-
* consolidate inputs. When the actual feerate is greater than the consolidate feerate, we will tend to make
|
|
623
|
-
* transactions which have the lowest fees.
|
|
624
|
-
*/
|
|
625
|
-
CFeeRate m_consolidate_feerate{DEFAULT_CONSOLIDATE_FEERATE};
|
|
598
|
+
bool m_split_coins{DEFAULT_SPLIT_COINS};
|
|
599
|
+
bool m_check_github{DEFAULT_CHECK_GITHUB};
|
|
626
600
|
|
|
627
601
|
/** The maximum fee amount we're willing to pay to prioritize partial spend avoidance. */
|
|
628
602
|
CAmount m_max_aps_fee{DEFAULT_MAX_AVOIDPARTIALSPEND_FEE}; //!< note: this is absolute fee, not fee rate
|
|
@@ -634,8 +608,6 @@ public:
|
|
|
634
608
|
* CWallet::TransactionChangeType for details).
|
|
635
609
|
*/
|
|
636
610
|
std::optional<OutputType> m_default_change_type{};
|
|
637
|
-
/** Absolute maximum transaction fee (in satoshis) used by default for the wallet */
|
|
638
|
-
CAmount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE};
|
|
639
611
|
|
|
640
612
|
size_t KeypoolCountExternalKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
|
641
613
|
bool TopUpKeyPool(unsigned int kpSize = 0);
|
|
@@ -744,8 +716,6 @@ public:
|
|
|
744
716
|
/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
|
|
745
717
|
bool AbandonTransaction(const uint256& hashTx);
|
|
746
718
|
|
|
747
|
-
/** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
|
|
748
|
-
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
|
|
749
719
|
|
|
750
720
|
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
|
|
751
721
|
static std::shared_ptr<CWallet> Create(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#define BITCOIN_WALLET_WALLETDB_H
|
|
8
8
|
|
|
9
9
|
#include <script/sign.h>
|
|
10
|
+
#include <wallet/context.h>
|
|
10
11
|
#include <wallet/db.h>
|
|
11
12
|
#include <wallet/walletutil.h>
|
|
12
13
|
#include <key.h>
|
|
@@ -25,7 +26,7 @@ class CKeyPool;
|
|
|
25
26
|
class CMasterKey;
|
|
26
27
|
class CWallet;
|
|
27
28
|
class CWalletTx;
|
|
28
|
-
struct WalletContext;
|
|
29
|
+
//struct WalletContext;
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Overview of wallet database classes:
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
static Mutex g_warnings_mutex;
|
|
16
16
|
static bilingual_str g_misc_warnings GUARDED_BY(g_warnings_mutex);
|
|
17
17
|
static bool fLargeWorkInvalidChainFound GUARDED_BY(g_warnings_mutex) = false;
|
|
18
|
+
std::string strMintWarning;
|
|
18
19
|
|
|
19
20
|
void SetMiscWarning(const bilingual_str& warning)
|
|
20
21
|
{
|
|
@@ -41,6 +42,13 @@ bilingual_str GetWarnings(bool verbose)
|
|
|
41
42
|
warnings_verbose.emplace_back(warnings_concise);
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
// peercoin: wallet lock warning for minting
|
|
46
|
+
if (strMintWarning != "")
|
|
47
|
+
{
|
|
48
|
+
warnings_concise = Untranslated(strMintWarning);
|
|
49
|
+
warnings_verbose.emplace_back(warnings_concise);
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
// Misc warnings like out of disk space and clock is wrong
|
|
45
53
|
if (!g_misc_warnings.empty()) {
|
|
46
54
|
warnings_concise = g_misc_warnings;
|
|
@@ -20,4 +20,5 @@ void SetfLargeWorkInvalidChainFound(bool flag);
|
|
|
20
20
|
*/
|
|
21
21
|
bilingual_str GetWarnings(bool verbose);
|
|
22
22
|
|
|
23
|
+
extern std::string strMintWarning;
|
|
23
24
|
#endif // BITCOIN_WARNINGS_H
|